Maison > Questions et réponses > le corps du texte
Supposons que j'ai un objet de clés de chaîne et de valeurs de chaîne et que je souhaite les écrire en tant que propriétés personnalisées CSS dans du HTML généré par le serveur. Comment puis-je le faire en toute sécurité ?
Ce que j'entends par sécurité
Pour plus de simplicité, je limiterai les clés pour autoriser uniquement les caractères de la classe [a-zA-Z0-9_-]
.
En lisant la spécification CSS et en effectuant quelques tests personnels, je pense que vous pouvez faire beaucoup de progrès pour obtenir la valeur en suivant ces étapes :
{([字符串外部的
à l'extérieur de la chaîne a une accolade fermante correspondante. Sinon, supprimez cette paire clé-valeur. 3C
转义 <
的所有实例,以及使用 3E
转义 >
. 3B
对 ;
pour vous échapper. J'ai proposé les étapes ci-dessus sur la base de cette spécification de syntaxe CSS
Pour le contexte, ces propriétés peuvent être utilisées par des styles définis par l'utilisateur que nous insérons ailleurs, mais le même objet est également utilisé comme données de modèle dans le modèle, il peut donc contenir un mélange de chaînes destinées au contenu et de chaînes attendues comme variables CSS. . J'ai l'impression que l'algorithme ci-dessus trouve un bon équilibre entre être très simple sans courir le risque de jeter trop de paires clé-valeur qui pourraient être utiles en CSS (même en permettant de futurs ajouts à CSS, mais je veux m'assurer de ne pas le faire). il ne manque rien
Voici un code JS montrant ce que je veux réaliser. obj
是有问题的对象,而 preprocessPairs
est une fonction qui prend l'objet et le prétraite, en supprimant/reformatant les valeurs comme décrit dans les étapes ci-dessus.
function generateThemePropertiesTag(obj) { obj = preprocessPairs(obj); return `<style> :root { ${Object.entries(obj).map(([key, value]) => { return `--theme-${key}: ${value};` }).join("\n")} } </style>` }
Alors quand on lui donne un objet comme celui-ci
{ "color": "#D3A", "title": "The quick brown fox" }
Je veux que le CSS ressemble à ceci :
:root { --theme-color: #D3A; --theme-title: The quick brown fox; }
Bien que --theme-title
soit une variable personnalisée assez inutile lorsqu'elle est utilisée en CSS, elle ne casse pas réellement la feuille de style car CSS ignore les propriétés qu'il ne comprend pas.
P粉8981078742023-09-07 21:34:20
Nous pourrions en fait simplement utiliser des expressions régulières et d'autres algorithmes sans avoir à nous appuyer sur un langage spécifique, j'espère que c'est ce dont vous avez besoin.
En déclarant que la clé de l'objet est à l'intérieur [a-zA-Z0-9_-]
, nous devons analyser la valeur d'une manière ou d'une autre.
Nous pouvons donc le diviser en catégories et voir ce que nous rencontrons (elles peuvent être légèrement simplifiées pour plus de clarté) :
'.*'
(chaîne entourée d'apostrophes ; gourmand) ".*"
(Chaîne entre guillemets doubles ; gourmande) [+-]?d+(.d+)?(%|[A-z]+)?
(entier et décimal, pourcentage optionnel ou avec unité) #[0-9A-f]{3,6}
(couleur)[A-z0-9_-]+
(Mots clés, dénomination des couleurs, "facilité d'entrée", etc.) ([w-]+)([^)]+)
(类似 url()
、calc()
fonction > etc.)J'imagine que vous pourriez effectuer un filtrage avant d'essayer d'identifier ces modèles. Peut-être que nous coupons d'abord la chaîne de valeur. Comme vous l'avez mentionné, <
和 >
可以在 preprocessPairs()
est échappé au début de la fonction car il n'apparaîtra comme aucun des modèles que nous avons ci-dessus. Si vous ne souhaitez pas que les points-virgules non échappés apparaissent n'importe où, vous pouvez également les échapper.
Nous pouvons ensuite essayer d'identifier ces modèles dans values, et pour chaque modèle, nous devrons peut-être réexécuter le filtrage. Nous nous attendons à ce que ces modèles soient séparés par quelques (ou deux) caractères d'espacement.
Cela devrait être bien d'inclure la prise en charge des chaînes multilignes, qui sont une nouvelle ligne échappée.
Nous devons réaliser que nous avons au moins deux contextes à filtrer : HTML et CSS. Quand on est dans l'attribut 元素中包含样式时,输入必须是安全的,同时它必须是有效的 CSS。幸运的是,您没有将 CSS 包含在元素的
style
, c'est donc un peu plus facile.
Les points 1 à 5 seront donc très simples et la plupart des valeurs seront couvertes par un simple filtrage et découpage depuis l'avant. Avec quelques ajouts (je ne sais pas quel impact sur les performances), il pourrait même effectuer des vérifications supplémentaires pour les unités, mots-clés, etc.
Mais par rapport à d’autres points, je pense que le défi relativement plus important est le point 6. Vous pourriez décider de simplement désactiver url()
,让您检查函数的输入,因此例如您可能想要转义分号,甚至可能通过微小的调整再次检查函数内的模式例如对于calc()
dans ce style personnalisé.
Dans l’ensemble, c’est mon avis. Avec quelques ajustements à ces expressions régulières, cela devrait compléter ce que vous faites déjà et vous donner autant de flexibilité que possible lors de la saisie de CSS, tout en vous évitant d'avoir à modifier votre code à chaque fois que vous modifiez une fonctionnalité CSS.
function preprocessPairs(obj) { // Catch-all regular expression // Explanation: // ( Start of alternatives // \w+\(.+?\)| 1st alternative - function // ".+?(?<!\)"| 2nd alternative - string with double quotes // '.+?(?<!\)'| 3rd alternative - string with apostrophes // [+-]?\d+(?:\.\d+)?(?:%|[A-z]+)?| 4th alternative - integer/decimal number, optionally per cent or with a unit // #[0-9A-f]{3,6}| 5th alternative - colour // [A-z0-9_-]+| 6th alternative - keyword // ''| 7th alternative - empty string // "" 8th alternative - empty string // ) // [\s,]* const regexA = /(\w+\(.+?\)|".+?(?<!\)"|'.+?(?<!\)'|[+-]?\d+(?:\.\d+)?(?:%|[A-z]+)?|#[0-9A-f]{3,6}|[A-z0-9_-]+|''|"")[\s,]*/g; // newObj contains filtered testObject const newObj = {}; // Loop through all object properties Object.entries(obj).forEach(([key, value]) => { // Replace <>; value = value.trim().replace('<', '\00003C').replace('>', '\00003E').replace(';', '\00003B'); // Use catch-all regex to split value into specific elements const matches = [...value.matchAll(regexA)]; // Now try to build back the original value string from regex matches. // If these strings are equal, the value is what we expected. // Otherwise it contained some unexpected markup or elements and should // be therefore discarded. // We specifically set to ignore all occurences of url() and @import let buildBack = ''; matches.forEach((match) => { if (Array.isArray(match) && match.length >= 2 && match[0].match(/url\(.+?\)/gi) === null && match[0].match(/@import/gi) === null) { buildBack += match[0]; } }); console.log('Compare\n'); console.log(value); console.log(buildBack); console.log(value === buildBack); if (value === buildBack) { newObj[key] = value; } }); return newObj; }
Commentez, discutez, critiquez et faites-moi savoir si j'ai oublié d'aborder un sujet qui vous intéresse particulièrement.
Avertissement : je ne suis pas l'auteur, le propriétaire, l'investisseur ou le contributeur des sources mentionnées ci-dessous. Il se trouve que je les utilise pour obtenir des informations.