regex 将无效的html标记替换为< and >to&lt;和&gt;

avwztpqn  于 2023-08-08  发布在  其他
关注(0)|答案(1)|浏览(94)

当在机器人的帮助下向Telegram发送消息时,使用html格式,这意味着当尝试发送消息时会出现错误,您需要将这些箭头替换为&lt;&gt;,但每次将它们写在一堆文本中都不方便,我想试着做一个定期的,将自动替换这些东西,但不接触有效的html标签,例如:
不需要替换的有效标记的示例

  1. <a href="tg://user?id=1">Bot</a>

字符串
需要替换的无效标记

  1. <argument>


下面是我尝试编写的代码,但最终无法正常工作

  1. import re
  2. def replace_invalid_tags(html_string):
  3. invalid_tag_pattern = r'<[^a-zA-Z/!?](.*?)>'
  4. fixed_html = re.sub(invalid_tag_pattern, r'&lt;\1&gt;', html_string)
  5. return fixed_html
  6. html_string = '<a href="#">Link</a> <argument1> <argument2>'
  7. fixed_html = replace_invalid_tags(html_string)
  8. print(fixed_html)

xzlaal3s

xzlaal3s1#

我个人建议您使用 Python HTML解析或清理库来完成这项工作。正则表达式很棒,我喜欢它们。但在某些情况下,我更喜欢使用经过良好测试的库,这些库是专门为解决问题而构建的。
我不是一个Python程序员,但主要是一个PHP程序员。在好的CMS项目中,您可以添加一些清理库,如HTMLPurifier并定义规则。
在你的例子中,一些标签应该被转换成HTML实体,以便显示为普通文本,而在其他一些情况下,标签必须保持原样。当然,一些属性和特定的标签也应该被删除(例如:<img onload="alert('xss attack')"<script>alert('bad')</script>。这是解析器或清理库将做一个更安全的工作。
假设允许使用这些标记:

  • <a>,具有href属性。可能不应允许其他属性。通常,我会删除style="font-size: 100px"
  • <strong><em>,不带属性。旧的<b><i>标签怎么样?我将它们分别转换为<strong><em>,因为它们可能对可读性有用,但在 Telegram 中不允许。

所有其他标签都应该转换为<var>(如果允许),内容转换为HTML特殊字符(<&lt;>&gt;)。如果需要的话,处理其他转换可能是安全的。
在Python中,我看到你可以使用html-sanitizer library
我看到可以定义一些预处理器函数,通常是在需要时转换一些标记。这意味着您可以创建一个函数,将所有未经授权的标记转换为<var><pre>标记,并使用找到的标记的转义等效HTML填充其内容。一些预构建的预处理器函数已经存在,例如bold_span_to_strong(),因此有一些示例可以解决您的问题。
一个查找无效标签的纯正则表达式解决方案可以这样做:

  1. <\s*/?\s*(?!(?:a|strong|em)\b)\w+[^>]*>

字符串
示例:https://regex101.com/r/xiZl1n/1
我接受可选的空格,结束标记的斜线,然后使用负向前看,以避免匹配您想要接受的标记。我在valid标签后面添加了-Boundary \b,以避免它接受以“a”字符开头的<argument>。我只想匹配完整的单词。
然后你可以决定如何处理你所有的比赛。如果你想直接用&lt;替换<,你可以这样做:
https://regex101.com/r/xiZl1n/3

编辑:句柄->>.<=>

我仍然相信解析器是最好的选择。但是你问是否可以修改正则表达式来处理更多的情况。我个人不认为一个正则表达式可以做到这一点。当然也会不安全。
但正如我所评论的,你可以尝试几个步骤:
1.如果您认为值得,请将<i>标记转换为<em>,将<b>标记转换为<strong>
1.找到所有有效的标签,如<a><strong><em>,并分别用[a][strong][em]替换它们。这可以通过以下模式来完成:

  1. <\s*(/?)\s*(?=(?:a|strong|em)\b)(\w+[^>]*)>


并替换为[\1\2]。正在运行:https://regex101.com/r/xiZl1n/4

  1. \[\s*(/?)\s*((?:a|strong|em)\b)([^\]]*)\]


并替换为<\1\2\3>。正在运行:https://regex101.com/r/xiZl1n/8
在该步骤中,捕获组n°3包含所有标签属性。这是你可以过滤它,只接受一些特定的,如hrefidtitle,但删除所有其他(例如:classstyleonclick)。
使i标志区分大小写可能很重要。这就是它在 JavaScript 中的样子:

  1. const input = `<a href="https://www.example.com/page">Example page</a> is a <strong>valid</strong> tag.
  2. < EM class="something"> is also allowed</em>
  3. But <param> or <argument> should be escaped.
  4. This also: <br/> <
  5. br /> <img onload="alert('xss')" src="http://images.com/nice-cat.jpg" alt="Nice cat" />
  6. <script type="text/javascript">alert('Bad JS code')</` + `script>
  7. <a href="javascript:alert('XSS attack')" onclick="alert('xss')">A bad link</a>
  8. <a href = http://test.com title="This is just a test">test.com</a>
  9. Turn left <- or turn right ->
  10. Also, accept this => or a smiley >.<
  11. Accept <B>bold</B> and <i style="color:green">italic without style</i> converted to new tags.
  12. Also strip <b href="https://www.google.com">wrong attributes</b>`;
  13. // Attributes to drop are all design changes done with classes or style
  14. // and all attributes such as onload, onclick, etc.
  15. const regexAttributeToDrop = /^(?:style|class|on.*)$/i;
  16. // The attributes which can have a URL.
  17. const regexAttributeWithURL = /^(?:href|xlink:href|src)$/i;
  18. // Only accept absolute URLs and not bad stuff like javascript:alert('xss')
  19. const regexValidURL = /^(https?|ftp|mailto|tel):/i;
  20. /**
  21. * Filter tag attributes, based on the tag name, if provided.
  22. *
  23. * @param string attributes All the attributes of the tag.
  24. * @param string tagName Optional tag name (in lowercase).
  25. * @return string The filtered string of attributes.
  26. */
  27. function filterAttributes(attributes, tagName = '') {
  28. // Regex for attribute: $1 = attribute name, $2 = value, $3 = simple/double quote or nothing.
  29. const regexAttribute = /\s*([\w-]+)\s*=\s*((?:(["']).*?\3|[^"'=<>\s]+))/g;
  30. attributes = attributes.replace(regexAttribute, function (attrMatch, attrName, attrValue, quote) {
  31. // Don't keep attributes that can change the rendering or run JavaScript.
  32. if (name.match(regexAttributeToDrop)) {
  33. return '';
  34. }
  35. // Not an attribute to drop.
  36. else {
  37. // If the attribute is "href" or "xlink:href" then only accept full URLs
  38. // with the correct protocols and only for <a> tags.
  39. if (attrName.match(/^(?:xlink:)?href$/i)) {
  40. // If it's not a link then drop the attribute.
  41. if (tagName !== 'a') {
  42. return '';
  43. }
  44. // The attribute value can be quoted or not so we'll remove them.
  45. const url = attrValue.replace(/^['"]|['"]$/g, '');
  46. // If the URL is valid.
  47. if (url.match(regexValidURL)) {
  48. return ` ${attrName}="${url}"`;
  49. }
  50. // Invalid URL: drop href and notify visually.
  51. else {
  52. return ' class="invalid-url" title="Invalid URL"';
  53. }
  54. }
  55. // All other attributes: nothing special to do.
  56. else {
  57. return ` ${attrName}=${attrValue}`;
  58. }
  59. }
  60. });
  61. // Clean up: trim spaces around. If it's not empty then just add a space before.
  62. attributes = attributes.trim();
  63. if (attributes.length) {
  64. attributes = ' ' + attributes;
  65. }
  66. return attributes;
  67. }
  68. const steps = [
  69. {
  70. // Replace <b> by <strong>.
  71. search: /<\s*(\/?)\s*(b\b)([^>]*)>/gi,
  72. replace: '<$1strong>' // or '<$1strong$3>' to keep the attributes.
  73. },
  74. {
  75. // Replace <i> by <em>.
  76. search: /<\s*(\/?)\s*(i\b)([^>]*)>/gi,
  77. replace: '<$1em>' // or '<$1em$3>' to keep the attributes.
  78. },
  79. {
  80. // Transform accepted HTML tags into bracket tags.
  81. search: /<\s*(\/?)\s*(?=(?:a|strong|em)\b)(\w+[^>]*)>/gi,
  82. replace: '[$1$2]'
  83. },
  84. {
  85. // Replace "<" by "&lt;".
  86. search: /</g,
  87. replace: '&lt;'
  88. },
  89. {
  90. // Replace ">" by "&gt;".
  91. search: />/g,
  92. replace: '&gt;'
  93. },
  94. {
  95. // Transform the accepted tags back to correct HTML.
  96. search: /\[\s*(\/?)\s*((?:a|strong|em)\b)([^\]]*)\]/gi,
  97. // For the replacement, we'll provide a callback function
  98. // so that we can alter the attributes if needed.
  99. replace: function (fullMatch, slash, tagName, attributes) {
  100. // Convert the tag name to lowercase.
  101. tagName = tagName.toLowerCase();
  102. // If the slash group is empty then it's the opening tag.
  103. if (slash === '') {
  104. attributes = filterAttributes(attributes, tagName);
  105. return '<' + tagName + attributes + '>';
  106. }
  107. // The closing tag.
  108. else {
  109. return '</' + tagName + '>';
  110. }
  111. }
  112. },
  113. {
  114. // Optional: inject <br /> tags at each new lines to preserve user input.
  115. search: /(\r?\n)/gm,
  116. replace: '<br />$1'
  117. }
  118. ];
  119. let output = input;
  120. steps.forEach((step, i) => {
  121. output = output.replace(step.search, step.replace);
  122. });
  123. document.getElementById('input').innerText = input;
  124. document.getElementById('output').innerText = output;
  125. document.getElementById('rendered-output').innerHTML = output;
  1. body {
  2. font-family: Georgia, "Times New Roman", serif;
  3. margin: 0;
  4. padding: 0 1em 1em 1em;
  5. }
  6. p:first-child { margin-top: 0; }
  7. pre {
  8. background: #f8f8f8;
  9. border: 1px solid gray;
  10. padding: .5em;
  11. overflow-x: scroll;
  12. }
  13. .box {
  14. background: white;
  15. box-shadow: 0 0 .5em rgba(0, 0, 0, 0.1);
  16. padding: 1em;
  17. }
  18. .invalid-url {
  19. color: darkred;
  20. }
  1. <h1>Quick &amp; dirty HTML filtering with regular expressions</h1>
  2. <p>Input:</p>
  3. <pre id="input"></pre>
  4. <p>Output:</p>
  5. <pre id="output"></pre>
  6. <p>Rendered output:</p>
  7. <div id="rendered-output" class="box"></div>
展开查看全部

相关问题