Conflitto con la soluzione RGPD di Quantcast "choice" e ember.js

Ciao,
quindi questo è un bug piuttosto specifico, ma il suo impatto potrebbe andare ben oltre questo caso particolare, quindi ho una domanda al riguardo.

(Anche, mi scuso per il mio inglese, sono francese, quindi non sono affatto un madrelingua…)

Ma prima, lasciatemi spiegare il contesto.
Utilizzo Discourse da un po’ di tempo per un forum francese sul Raspberry Pi (forum.raspberry-pi.fr). Questo forum utilizza la gestione della pubblicità (themoneytizer). Come probabilmente sapete, l’Europa ci obbliga a implementare il RGPD per la protezione della privacy degli utenti. Il principale attore (almeno in Francia) per il consenso RGPD è Quantcast e la loro soluzione “Choice”.

Quindi, ho utilizzato la soluzione di Quantcast per un bel po’ di tempo, senza problemi, fino a quando recentemente non ho notato che il pulsante “Accetta tutto” non funziona più correttamente. Al clic, non succede nulla e guardando nella console degli sviluppatori ho ottenuto questo errore: “Uncaught TypeError: can’t define property “status”: Function is not extensible”.

Cosa succede (almeno per quanto ho capito io):
Fidatevi quando dico che mi ci è voluto un bel po’ di tempo per trovare la fonte del problema. A quanto pare, Ember.js (con cui non sono molto familiare) estende alcuni oggetti nativi di JavaScript, come Array, String e Function. E per qualche motivo sembra anche impedire in qualche modo l’estensione di questi oggetti in un certo modo (non ho ancora capito completamente questa parte).

La soluzione di Quantcast, da parte sua, cerca, probabilmente nella funzione FunctionAcceptAll (spiegando perché il bug si verifica solo quando si clicca sul pulsante “accetta tutto” e sul pulsante “rifiuta tutto”), di estendere un oggetto, presumo un array, il cui comportamento normale è stato modificato da Ember.js.

Dopo molte ricerche per comprendere questo bug, ho anche scoperto che è possibile modificare il comportamento di Ember.js per non estendere i prototipi di JavaScript, come spiegato in questa pagina https://guides.emberjs.com/release/configuring-ember/disabling-prototype-extensions/.

Ho fatto alcuni test e il bug scompare se aggiungo la riga window.EmberENV.EXTEND_PROTOTYPES = {String: true, Array: false}; nel file _ember_jquery-189e46ebcb33594b835e782fd1ce916ec750bc0cf980ebc4fb7796649161a18d.js dopo la riga window.EmberENV.FORCE_JQUERY = true;.

Per coloro che volessero provare, potete dare un’occhiata alla pagina /tst/index.html sul forum (potrebbe essere necessario un IP europeo affinché lo script venga avviato, non ne ho idea).
Ora, penso di avervi fornito tutte le informazioni possibili.

Quindi, ora ecco la mia domanda.
Anche se questo è un bug piuttosto specifico, il RGPD è sempre più presente in Europa in questo momento e non diventerà più facile.
Quantcast è in una posizione quasi monopolistica, almeno per gli attori che non possono permettersi di pagare centinaia di dollari per implementare il RGPD. Questo bug impedisce qualsiasi utilizzo di Quantcast, e quindi della pubblicità su Discourse in Europa, che mi sembra un grosso problema.
Inoltre, anche se ho trovato il bug solo usando Quantcast, questo tipo di bug potrebbe effettivamente verificarsi per molti script di terze parti che dobbiamo incorporare per la pubblicità o altro, sui quali non abbiamo alcun controllo e che fanno affidamento sul comportamento “normale” di JavaScript per gli oggetti Array, String e Function.

Non conosco abbastanza il codice di Discourse, quindi vi chiedo: le proprietà aggiunte da Ember.js agli oggetti Array, String e Function (vedi il link sopra) sono utilizzate da Discourse o no? Se non lo sono, forse dovremmo considerare di disabilitare le estensioni dei prototipi da Ember.js, per prevenire effetti collaterali come questo?

Spero che qualcuno possa darmi informazioni a riguardo,
grazie

Al momento questa funzionalità non è supportata, poiché facciamo affidamento sulle estensioni. Forse in una versione futura non sarà più necessario. @eviltrout può fornire ulteriori dettagli.

Non sono sicuro di cosa fare in questo caso, ma credo dovremmo trovare una soluzione alternativa. Sono sorpreso che ciò violi il RGPD; forse potresti aprire un ticket con Quantcast per discuterne e collegarlo qui?

Come stai aggiungendo gli script necessari per Quantcast? Li stai inserendo tramite un tag script con un attributo src (esterno) come questo:

<script src="foo"></script>

Oppure li stai inserendo in linea come in questo caso?

<script>
  alert("Ciao Mondo!");
</script>

Ciao,
grazie per le vostre risposte.

@sam Ho aperto un ticket con Quantcast il giorno in cui ho creato questo argomento e oggi mi hanno risposto; sembra che stiano esaminando la questione e speriamo che trovino una soluzione. Penso che per loro sia una correzione piuttosto semplice, dato che il pulsante “accetta selezione” funziona correttamente. Spero che considerino Discourse e Ember.js strumenti abbastanza utilizzati da meritare una correzione dedicata.

@Johani In realtà non aggiungo io stesso gli script di Quantcast; themoneytizer fornisce uno script che si occupa di inserirli per me (oltre a cercare eventi tcf2, ecc.). Se vuoi, puoi dare un’occhiata a questo script qui.

Questo script sembra aggiungere un nuovo elemento prima del primo script della pagina. Non ne sono certo al 100%, ma ho visto quasi esattamente questo script in alcuni esempi della documentazione di Quantcast, quindi presumo sia uno script abbastanza comune per l’integrazione di Quantcast Choice.

Grazie ancora per il vostro tempo.

Non penso che potremo rimuovere in sicurezza i prototipi degli array a breve. Nel corso degli anni si sono rivelati innocui e offrono molta comodità.

Forse potresti contattare Quantcast per ottenere una versione degli script che non dipenda dai prototipi degli array?

Ehi a tutti,
ho alcune nuove informazioni per voi e ho trovato una soluzione a questo problema.

Quindi, oggi ho scavato di nuovo in questo bug e penso di aver trovato ulteriori dettagli.

Analizzando il file cmp2ui-fr.js di Quantcast, sono riuscito a individuare dove si verifica il bug; si trova in questa funzione (abbiamo solo la versione minificata):

function(t){for(var n in t){t[n].status=e;}}

Come vedete, questa funzione utilizza un for..in, e la variabile t è un array. Come avevamo spiegato in precedenza, Ember.js estende l’Array nativo di JavaScript. Sembra che una delle modifiche apportate sia l’aggiunta di una voce _super.

La voce _super punta a una funzione ROOT(), che sembra riferirsi a _utils.ROOT in Ember.js (forse questo vi suona familiare, ma a me no ^^). Questa funzione ROOT() non è estendibile.

Apparentemente, questa proprietà _super viene considerata enumerabile, il che significa che quando si utilizza un ciclo for..in su un array, la voce _super viene trattata come una voce “normale” (a differenza, ad esempio, di values, bind, valueOf, ovvero praticamente tutte le funzioni di un oggetto array).

Penso che questo sia un bug di Ember.js e non un comportamento desiderabile.

Quindi, sono riuscito a rendere il bug molto riproducibile. Per farlo, basta creare un array di oggetti, come questo:

var objs = [{'key':'val'},{'key':'val'}];

Poi, creare una funzione in strict mode che itera su un array e imposta una nuova proprietà per ogni voce:

var tst_func = function (objs){'use strict';for(var i in objs){objs[i].newproperty = true }};

Infine, chiamate semplicemente questa funzione sull’array di oggetti:

tst_func(objs);

Dovreste ottenere un errore Uncaught TypeError: can't define property "newproperty": Function is not extensible.

Osservando questo, mi viene da pensare che il bug sia molto probabilmente già presente in ambienti reali e non sia affatto specifico di Quantcast. Fondamentalmente, chiunque provi a utilizzare for..in si espone a comportamenti incoerenti o a bug critici.

Per me, la responsabilità non ricade davvero su Discourse o Quantcast, ma chiaramente su Ember.js. Tuttavia, questo non cambia il fatto che dobbiamo trovare una soluzione finché non verrà corretto in Ember.js ^^.

La buona notizia è che penso di aver trovato un modo per risolvere il problema.

Un modo per farlo è inserire una riga if (!objs.hasOwnProperty(i)) {continue}; in ogni ciclo for..in (probabilmente anche in forEach, for...of, ecc.). Questo non modifica il comportamento strano dell’Array _super, ma previene localmente l’accesso ad esso. Ovviamente, ciò significa che non funziona per eventuali script esterni su cui non abbiamo controllo, come nel mio caso particolare con Quantcast.

Un altro metodo, che ritengo sia la strada da seguire, è modificare il prototipo dell’Array di JavaScript (quindi di fatto sovrascriviamo la sovrascrittura di Ember.js ^^) per rendere _super non enumerabile. Per farlo, dobbiamo eseguire questa riga di JavaScript DOPO che Ember.js è stato chiamato e valutato:

//Rendi _super non enumerabile per prevenire bug tra Ember.js e for..in
Object.defineProperty(Array.prototype, '_super', {'enumerable': false});

Il secondo metodo mantiene la possibilità di utilizzare direttamente _super, come dovrebbe essere previsto, e ne previene la comparsa nei cicli. Tuttavia, non posso garantire che questo comportamento strano non venga utilizzato da alcuna funzione interna di Ember.js o da plugin esterni.

Analizzando il file _ember_jquery-189e46ebcb33594b835e782fd1ce916ec750bc0cf980ebc4fb7796649161a18d.js, ho notato alcune righe specifiche di Discourse, come:

var ALIASES = {
    "ember-addons/ember-computed-decorators":
      "discourse-common/utils/decorators",
    "discourse/lib/raw-templates": "discourse-common/lib/raw-templates",
    "preload-store": "discourse/lib/preload-store",
    "fixtures/user_fixtures": "discourse/tests/fixtures/user-fixtures",
  };
  var ALIAS_PREPEND = {
    fixtures: "discourse/tests/",
    helpers: "discourse/tests/",
  };

Forse potremmo aggiungere qui la nostra riga Object.defineProperty(Array.prototype, '_super', {'enumerable': false});?

Al momento ho risolto il bug aggiungendo le righe:
//Rendi _super non enumerabile per prevenire bug tra Ember.js e for..in
Object.defineProperty(Array.prototype, ‘_super’, {‘enumerable’: false});

Prima di chiamare lo script choice.js di Quantcast.

Penso che qualcuno possa risolvere il problema localmente creando un file ‘fix_ember.js’ con queste due righe, servendolo in modo statico tramite un reverse proxy (ad esempio Nginx) e aggiungendo una riga <script src="/fix_emmber.js"></script> nel footer del tema tramite personalizzazione. Scrivere direttamente lo script invece di creare un link non funziona a causa dell’estrazione degli script di Discourse (vedi Custom javascript in <head> disappear).

Spero che questo argomento sia d’aiuto ad altri. Domani aprirò un ticket su Ember.js per verificare se si tratta di un bug o di un comportamento molto strano ma deliberato.

Vi terrò aggiornati per vedere se dobbiamo includere una correzione in Discourse.

PS: Molti ringraziamenti ad Angus Croll da Extending JavaScript Natives – JavaScript, JavaScript…, il suo post mi è stato di grande aiuto!

Molte grazie @OsaAjani per averlo individuato. Abbiamo lo stesso identico problema con Quantcast Choice sul nostro forum Discourse e siamo bloccati alla versione 12 (che era l’ultima che non generava errori).

L’ultima versione è la 23 e avremmo molto desiderio di utilizzarla.

Restiamo in attesa di sapere come risolvere il problema il prima possibile.

Ciao @Terrapop, sono felice che questo argomento possa esserti utile. Al momento non ho ricevuto alcun feedback sul problema che ho aperto nel repository di ember.js ([Bug] Property "_super" is enumerable on Array, creating conflicts with external libs (ex : Quantcast Choice RGPD) · Issue #19289 · emberjs/ember.js · GitHub), né da Quantcast.

La buona notizia è che ho utilizzato la correzione descritta nel mio messaggio precedente, creando un file fix_ember.js con il codice:

Object.defineProperty(Array.prototype, '_super', {'enumerable': false});

E aggiungendo una riga:

<script defer src="/fix_emmber.js"></script>

Nel piè di pagina del tema, utilizzando le opzioni di personalizzazione dal pannello di amministrazione.

Questa soluzione risolve il problema e, finora, non ho rilevato effetti collaterali. Quindi, penso che tu possa utilizzare questa correzione con fiducia e passare alla versione 23 di Quantcast.

Abbiamo aggiunto la riga prima del caricamento di choice.js. Anche questo sembra funzionare correttamente.

Per l’altra soluzione: non abbiamo un reverse proxy davanti al nostro Discourse, quindi non siamo sicuri di come creare correttamente il file fix_ember.js. È possibile farlo all’interno di Docker? Qualche suggerimento?

Beh, se riesci a caricarlo prima di choice.js, è ancora meglio!

Sì, funziona. Non possiamo ringraziarti abbastanza: ci siamo grattati la testa per giorni su questo problema senza riuscire a individuarlo. Abbiamo scambiato 20 email con Quantcast, ma anche loro non sono riusciti a trovare il problema.

Bene, se hai un buon canale di comunicazione con Quantcast, potresti indicarli a questo post, così potranno risolverlo. L’ho fatto, ma poiché non sono effettivamente un loro cliente, l’assistenza non mi ha dato molte risposte.

Lo farò. Ottimo punto.