HTML non sottoposto a sanitizzazione nell'API Async Clipboard

A partire da Chrome 120, è disponibile una nuova opzione unsanitized negli appunti asincroni tramite Google Cloud CLI o tramite l'API Compute Engine. Questa opzione può essere utile in situazioni speciali con HTML, dove è necessario incollare i contenuti degli appunti identici a come erano quando sono stati copiati. Vale a dire, senza alcun passaggio intermedio di sanificazione comunemente utilizzato dai browser, per validi motivi, fai domanda. Scopri come utilizzarlo in questa guida.

Quando utilizzi API Async Clipboard, nella maggior parte dei casi, gli sviluppatori non devono preoccuparsi dell'integrità i contenuti negli appunti e possiamo presumere che ciò che scrivono nella Appunti (copia) è lo stesso di quello che riceveranno quando legge i dati da negli appunti (da incollare).

Questo è decisamente vero per i testi. Prova a incollare il seguente codice in DevTools e rimettere a fuoco la pagina immediatamente. (setTimeout() è necessario) in modo da avere tempo sufficiente per impostare lo stato attivo sulla pagina, come richiesto dal modello API Clipboard). Come puoi vedere, l'input è esattamente uguale all'output.

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);

Con le immagini, è un po' diverso. Per evitare le cosiddette attacchi di bomba di compressione, browser ricodificare immagini come i file PNG, ma le immagini di input e di output sono visivamente esattamente la stessa, pixel per pixel.

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);

Cosa succede con il testo HTML? Come avrai intuito, con l'HTML, la situazione è diversa. In questo caso, il browser ripulisce il codice HTML per evitare cose da accadere, ad esempio eliminando i tag <script> dal codice HTML (e altri come <meta>, <head> e <style>) e incorporare il codice CSS. Considera l'esempio seguente e provalo nella console DevTools. Potrai puoi notare che l'output differisce molto notevolmente dall'input.

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);

La sanificazione HTML in genere è un aspetto positivo. Non vuoi esporti ai problemi di sicurezza, consentendo nella maggior parte dei casi HTML non sottoposto a sanitizzazione. Là sono scenari in cui lo sviluppatore sa esattamente cosa sta facendo e in cui l'integrità dell'HTML in-stream e di output è fondamentale per la il funzionamento dell'app. In queste circostanze, hai due opzioni:

  1. Se hai il controllo sia di copiare che di incollare, ad esempio, dall'interno dell'app per incollarla nell'app, devi usare Formati personalizzati web per l'API Async Clipboard. Smetti di leggere qui e controlla l'articolo collegato.
  2. Se sei tu a controllare solo l'estremità di incollare nell'app, ma non quella di copia, ad esempio perché l'operazione di copia viene eseguita in un'app nativa che non supporta formati personalizzati per il web, devi utilizzare l'opzione unsanitized, che spiegati nel resto di questo articolo.

La sanificazione include, ad esempio, la rimozione di tag script, l'allineamento degli stili e verificando che l'HTML sia formattato correttamente. Questo elenco non è esaustivo e altro ancora potrebbero essere aggiunti in futuro.

Copia e incolla HTML non convalidato

Quando write() (copi) il codice HTML negli appunti con l'API Async Clipboard, il browser si assicura che sia nel formato corretto eseguendolo attraverso un parser DOM e serializzare la stringa HTML risultante, ma non viene eseguita alcuna sanificazione questo passaggio. Non devi fare nulla. Quando read() inserisci il codice HTML nella appunti da un'altra applicazione e la tua app web sta accettando il ad alta fedeltà e la necessità di eseguire la sanificazione nel tuo codice, puoi passare un oggetto opzioni al metodo read() con una proprietà unsanitized e il valore ['text/html']. In sostanza, ha il seguente aspetto: navigator.clipboard.read({ unsanitized: ['text/html'] }). Il seguente esempio di codice di seguito è quasi uguale a quella mostrata in precedenza, ma questa volta con l'unsanitized . Quando lo provi nella console DevTools, vedrai che l'input e l'output è lo stesso.

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);

Supporto dei browser e rilevamento delle funzionalità

Non esiste un modo diretto per verificare se la funzionalità è supportata, quindi la funzionalità il rilevamento si basa sull'osservazione del comportamento. Di conseguenza, il seguente esempio si basa sul rilevamento della persistenza di un tag <style>, che indica supporto, o è in linea, indica che non è supportato. Tieni presente che perché funzioni, la pagina deve avere già ottenuto gli appunti autorizzazione.

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);
};

Demo

Per vedere l'opzione unsanitized in azione, consulta le di Google su Glitch e dai un'occhiata codice sorgente.

Conclusioni

Come descritto nell'introduzione, la maggior parte degli sviluppatori non dovrà mai preoccuparsi la sanificazione degli appunti e può funzionare solo con le opzioni di sanificazione predefinite dal browser. Nei rari casi in cui gli sviluppatori devono prestare attenzione, L'opzione unsanitized esiste.

Ringraziamenti

Questo articolo è stato esaminato da Anupam Snigdha e Rachel Andrew. L'API è stata specificata e implementato dal team di Microsoft Edge.