diff --git a/README.md b/README.md index 6bcad2b..2d95cba 100644 --- a/README.md +++ b/README.md @@ -351,6 +351,19 @@ Each threshold entry is used to define the upper bounds of the threshold. If you upper bound for your version number threshold you will need to provide an extreme upper bound - in this example it is `999.0.0`. +### Escaping + +Badges are generated as .svg files, which utilize an XML-based markup language. Consequently, +any HTML characters present in badge labels or values must be escaped to ensure proper +representation. If you need to disable escaping, the following options are available: + +- Python API + - `escape_label=False` + - `escape_value=False` +- CLI + - `--no-escape-label` + - `--no-escape-value` + ### Examples #### Pylint using template diff --git a/anybadge/badge.py b/anybadge/badge.py index d21e1df..0e25473 100644 --- a/anybadge/badge.py +++ b/anybadge/badge.py @@ -2,6 +2,7 @@ import os from collections import OrderedDict from pathlib import Path from typing import Dict, Type, Optional, Union +import html from . import config from .colors import Color @@ -122,6 +123,8 @@ class Badge: value_format: Optional[str] = None, text_color: Optional[str] = None, semver: Optional[bool] = False, + escape_label: Optional[bool] = True, + escape_value: Optional[bool] = True, ): """Constructor for Badge class.""" # Set defaults if values were not passed @@ -209,6 +212,9 @@ class Badge: self.use_max_when_value_exceeds = use_max_when_value_exceeds self.mask_str = self.__class__._get_next_mask_str() + self.escape_label = escape_label + self.escape_value = escape_value + def __repr__(self) -> str: """Return a representation of the Badge object instance. @@ -333,6 +339,20 @@ class Badge: else: return self.template + @property + def encoded_label(self) -> str: + if self.escape_label: + return html.escape(self.label) + else: + return self.label + + @property + def encoded_value(self) -> str: + if self.escape_value: + return html.escape(self.value_text) + else: + return self.value_text + @property def semver_version(self) -> Version: """The semantic version represented by the value string. @@ -638,8 +658,8 @@ class Badge: badge_text.replace("{{ badge width }}", str(self.badge_width)) .replace("{{ font name }}", self.font_name) .replace("{{ font size }}", str(self.font_size)) - .replace("{{ label }}", self.label) - .replace("{{ value }}", self.value_text) + .replace("{{ label }}", self.encoded_label) + .replace("{{ value }}", self.encoded_value) .replace("{{ label anchor }}", str(self.label_anchor)) .replace("{{ label anchor shadow }}", str(self.label_anchor_shadow)) .replace("{{ value anchor }}", str(self.value_anchor)) diff --git a/anybadge/cli.py b/anybadge/cli.py index 1f76bd2..97096a0 100644 --- a/anybadge/cli.py +++ b/anybadge/cli.py @@ -155,6 +155,18 @@ examples: default=False, help="Treat value and thresholds as semantic versions.", ) + parser.add_argument( + "--no-escape-label", + action="store_true", + default=False, + help="Do not escape the label text.", + ) + parser.add_argument( + "--no-escape-value", + action="store_true", + default=False, + help="Do not escape the value text.", + ) parser.add_argument( "args", nargs=argparse.REMAINDER, @@ -217,6 +229,8 @@ def main(args=None): value_format=args.value_format, text_color=args.text_color, semver=args.semver, + escape_label=not args.no_escape_label, + escape_value=not args.no_escape_value, ) if args.file: