Skip to content

Commit

Permalink
fix: prevent html tags from input (#17147)
Browse files Browse the repository at this point in the history
  • Loading branch information
miketheman authored Nov 21, 2024
1 parent ff45489 commit b837b1d
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 3 deletions.
1 change: 1 addition & 0 deletions requirements/main.in
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ lxml
more-itertools
msgpack
natsort
nh3
opensearch-py
orjson
packaging>=24.2
Expand Down
4 changes: 3 additions & 1 deletion requirements/main.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1410,7 +1410,9 @@ nh3==0.2.18 \
--hash=sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50 \
--hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \
--hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe
# via readme-renderer
# via
# -r requirements/main.in
# readme-renderer
openapi-core==0.19.4 \
--hash=sha256:1150d9daa5e7b4cacfd7d7e097333dc89382d7d72703934128dcf8a1a4d0df49 \
--hash=sha256:38e8347b6ebeafe8d3beb588214ecf0171874bb65411e9d4efd23cb011687201
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/packaging/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,16 @@ def test_summary_too_long(self, pyramid_request):

assert not form.validate()
assert "summary" in form.errors

def test_summary_contains_html_tags(self, pyramid_request):
pyramid_request.POST = MultiDict(
{
"inspector_link": self.inspector_link,
"summary": '<img src="https://example.com/image.png">',
}
)

form = SubmitMalwareObservationForm(pyramid_request.POST)

assert not form.validate()
assert "summary" in form.errors
46 changes: 45 additions & 1 deletion tests/unit/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
from webob.multidict import MultiDict
from wtforms.validators import ValidationError

from warehouse.forms import PasswordStrengthValidator, SetLocaleForm, URIValidator
from warehouse.forms import (
PasswordStrengthValidator,
PreventHTMLTagsValidator,
SetLocaleForm,
URIValidator,
)


class TestURIValidator:
Expand Down Expand Up @@ -83,6 +88,45 @@ def test_invalid_password(self, password, expected):
assert str(exc.value) == expected


class TestPreventHTMLTagsValidator:
@pytest.mark.parametrize(
"inbound_data",
[
"A link https://example.com",
"query string https://example.com?query=string",
"anchor https://example.com#fragment",
"qs and anchor https://example.com?query=string#fragment",
"path, qs, anchor https://example.com/path?query=string#fragment",
"A comment with a > character",
"A comment with a < character",
"A comment with a & character",
"A comment with a ' character",
'A comment with a " character',
],
)
def test_valid(self, inbound_data):
validator = PreventHTMLTagsValidator()
validator(pretend.stub(), pretend.stub(data=inbound_data))

def test_invalid(self):
validator = PreventHTMLTagsValidator()
with pytest.raises(ValidationError) as exc:
validator(
pretend.stub(), pretend.stub(data="<img src='https://example.com'>")
)

assert str(exc.value) == "HTML tags are not allowed"

def test_custom_message(self):
validator = PreventHTMLTagsValidator(message="No HTML allowed")
with pytest.raises(ValidationError) as exc:
validator(
pretend.stub(), pretend.stub(data="<img src='https://example.com'>")
)

assert str(exc.value) == "No HTML allowed"


class TestSetLocaleForm:
def test_validate(self):
form = SetLocaleForm(MultiDict({"locale_id": "es"}))
Expand Down
23 changes: 23 additions & 0 deletions warehouse/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from typing import TYPE_CHECKING

from nh3 import is_html
from wtforms import Form as BaseForm, StringField
from wtforms.validators import InputRequired, ValidationError
from zxcvbn import zxcvbn

from warehouse.i18n import KNOWN_LOCALES
from warehouse.utils.http import is_valid_uri

if TYPE_CHECKING:
from wtforms.fields import Field


class URIValidator:
def __init__(
Expand Down Expand Up @@ -75,6 +83,21 @@ def __call__(self, form, field):
raise ValidationError(msg)


class PreventHTMLTagsValidator:
"""
Validate the field to ensure that it does not contain any HTML tags.
"""

def __init__(self, message: str | None = None):
if message is None:
message = "HTML tags are not allowed"
self.message = message

def __call__(self, form: BaseForm, field: Field):
if is_html(field.data):
raise ValidationError(self.message)


class SetLocaleForm(BaseForm):
__params__ = ["locale_id"]

Expand Down
2 changes: 1 addition & 1 deletion warehouse/locale/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ msgstr ""
msgid "Top-level pipeline file path cannot start or end with /"
msgstr ""

#: warehouse/packaging/forms.py:26
#: warehouse/packaging/forms.py:27
msgid "Provide an Inspector link to specific lines of code."
msgstr ""

Expand Down
2 changes: 2 additions & 0 deletions warehouse/packaging/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import wtforms

from warehouse.forms import PreventHTMLTagsValidator
from warehouse.i18n import localize as _


Expand All @@ -32,6 +33,7 @@ class SubmitMalwareObservationForm(wtforms.Form):
validators=[
wtforms.validators.InputRequired(),
wtforms.validators.Length(min=10, max=2000),
PreventHTMLTagsValidator(),
],
)

Expand Down

0 comments on commit b837b1d

Please sign in to comment.