diff --git a/securedrop/sass/source.sass b/securedrop/sass/source.sass
index d3a0ee331c..c2c4b25b5f 100644
--- a/securedrop/sass/source.sass
+++ b/securedrop/sass/source.sass
@@ -34,37 +34,33 @@
text-align: center
width: 80%
-@media only screen and (max-width: 880px)
+@media only screen
#codename-hint
- width: 100%
+ summary
+ cursor: pointer
-#codename-hint-visible
- .visible-codename
- margin: auto
- padding: auto
- height: auto
- overflow: visible
+ &:after
+ content: "Show"
+ display: block
+ float: right
- .hidden-codename
- margin: 0
- padding: 0
- height: 0
- overflow: hidden
+ text-decoration: none
+ color: $color_securedrop_blue
+ border-bottom: 1px solid rgba(0, 117, 247, 0.4)
- &:target
+ &:hover, &:active
+ &:after
+ color: $color_grimace_blue
+ border-bottom: 1px solid rgba(42, 49, 157, 1)
- .visible-codename
- margin: 0
- padding: 0
- height: 0
- overflow: hidden
- border: 0
+ &[open]
+ summary
+ &:after
+ content: "Hide"
- .hidden-codename
- margin: auto
- padding: auto
- height: auto
- overflow: visible
+@media only screen and (max-width: 880px)
+ #codename-hint
+ width: 100%
input.codename-box
width: 100%
@@ -72,6 +68,7 @@ input.codename-box
font-weight: bold
.codename
+ background-color: transparent
font-family: monospace
letter-spacing: 0.15em
font-weight: normal
@@ -160,6 +157,7 @@ p#codename
font-size: 10px
font-weight: bold
padding: 5px
+ margin: 0
a.delete
float: right
@@ -217,7 +215,7 @@ p#codename
margin: 0 2%
padding: 5px 15px
- output#no-replies
+ #no-replies
display: block
font-weight: bold
text-align: center
diff --git a/securedrop/source_app/forms.py b/securedrop/source_app/forms.py
index 6fb47b6329..90701dc035 100644
--- a/securedrop/source_app/forms.py
+++ b/securedrop/source_app/forms.py
@@ -11,31 +11,21 @@
class LoginForm(FlaskForm):
- codename = PasswordField(
- "codename",
- validators=[
- InputRequired(message=gettext("This field is required.")),
- Length(
- 1,
- PassphraseGenerator.MAX_PASSPHRASE_LENGTH,
- message=gettext(
- "Field must be between 1 and "
- "{max_codename_len} characters long.".format(
- max_codename_len=PassphraseGenerator.MAX_PASSPHRASE_LENGTH
- )
- ),
- ),
- # Make sure to allow dashes since some words in the wordlist have them
- Regexp(r"[\sA-Za-z0-9-]+$", message=gettext("Invalid input.")),
- ],
- )
+ codename = PasswordField('codename', validators=[
+ InputRequired(message=gettext('This field is required.')),
+ Length(1, PassphraseGenerator.MAX_PASSPHRASE_LENGTH,
+ message=gettext(
+ 'Field must be between 1 and '
+ '{max_codename_len} characters long.'.format(
+ max_codename_len=PassphraseGenerator.MAX_PASSPHRASE_LENGTH))),
+ # Make sure to allow dashes since some words in the wordlist have them
+ Regexp(r'[\sA-Za-z0-9-]+$', message=gettext('Invalid input.'))
+ ])
class SubmissionForm(FlaskForm):
- msg = TextAreaField("msg",
- render_kw={"aria-label": gettext("Write a message."),
- "placeholder": gettext("Write a message."),
- })
+ msg = TextAreaField("msg", render_kw={"placeholder": gettext("Write a message."),
+ "aria-label": gettext("Write a message.")})
fh = FileField("fh", render_kw={"aria-label": gettext("Select a file to upload.")})
def validate_msg(self, field: wtforms.Field) -> None:
diff --git a/securedrop/source_templates/base.html b/securedrop/source_templates/base.html
index f309e4514d..bfb75f8115 100644
--- a/securedrop/source_templates/base.html
+++ b/securedrop/source_templates/base.html
@@ -37,7 +37,7 @@
{{ gettext("We're sorry, our SecureDrop is currently offline.") }}
{% if 'logged_in' in session %}
- {{ gettext('LOG OUT') }}
+ {{ gettext('LOG OUT') }}
{% endif %}
diff --git a/securedrop/source_templates/first_submission_flashed_message.html b/securedrop/source_templates/first_submission_flashed_message.html
index 53926883cd..629e47db72 100644
--- a/securedrop/source_templates/first_submission_flashed_message.html
+++ b/securedrop/source_templates/first_submission_flashed_message.html
@@ -1,7 +1,7 @@
{{ gettext('Success!') }}
{{ gettext('Thank you for sending this information to us. Please check back later for replies.') }}
-
+
{{ gettext('Forgot your codename?') }}
diff --git a/securedrop/source_templates/flashed.html b/securedrop/source_templates/flashed.html
index deeabfe20b..2ff82ac231 100644
--- a/securedrop/source_templates/flashed.html
+++ b/securedrop/source_templates/flashed.html
@@ -4,7 +4,7 @@
{% for category, message in messages %}
{% if category != 'banner-warning' %}
-
+
{{ message }}
{% endif %}
diff --git a/securedrop/source_templates/generate.html b/securedrop/source_templates/generate.html
index 310f39f12a..fcf8055e90 100644
--- a/securedrop/source_templates/generate.html
+++ b/securedrop/source_templates/generate.html
@@ -7,9 +7,9 @@ {{ gettext('Welcome') }}
{{ gettext('This codename is what you will use in future visits to receive messages from our team in response to what you submit on the next screen.') }}
-
- {{ gettext('Codename') }}
- {{ codename }}
+
+ {{ gettext('Codename') }}
+ {{ codename }}
diff --git a/securedrop/source_templates/index.html b/securedrop/source_templates/index.html
index fd3bd2038a..d0999e1524 100644
--- a/securedrop/source_templates/index.html
+++ b/securedrop/source_templates/index.html
@@ -39,7 +39,6 @@
diff --git a/securedrop/source_templates/login.html b/securedrop/source_templates/login.html
index c758e40500..7f6b6d7907 100644
--- a/securedrop/source_templates/login.html
+++ b/securedrop/source_templates/login.html
@@ -24,10 +24,10 @@ {{ gettext('Enter Codename') }}
diff --git a/securedrop/source_templates/logout.html b/securedrop/source_templates/logout.html
index dcdf020716..5fcab4f7e0 100644
--- a/securedrop/source_templates/logout.html
+++ b/securedrop/source_templates/logout.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block body %}
- {{ gettext('LOG IN') }}
+ {{ gettext('LOG IN') }}
{{ gettext('One more thing...') }}
diff --git a/securedrop/source_templates/lookup.html b/securedrop/source_templates/lookup.html
index 6bc27de258..614db14a56 100644
--- a/securedrop/source_templates/lookup.html
+++ b/securedrop/source_templates/lookup.html
@@ -2,21 +2,10 @@
{% block body %}
{% if new_user %}
-
-
-
{{ gettext('Remember, your codename is:') }}
- {# ARIA-HIDDEN violates axe rule aria-hidden-focus, because we (a) want to
- hide the superfluous "show"/"hide" links from screen-readers, (b) do not
- want to remove these elements from sequential navigation with TABINDEX="-1",
- and (c) do not have recourse in the Source Interface to scripting the
- ARIA-HIDDEN value dynamically. Cf. #6031. #}
-
{{ gettext('Show') }}
-
-
{{ gettext('Hide') }}
-
{{ codename }}
-
-
-
+
+ {{ gettext('Remember, your codename is:') }}
+ {{ codename }}
+
{% endif %}
@@ -54,13 +43,13 @@
{{ gettext('Submit Messages') }}
@@ -72,23 +61,23 @@ {{ gettext('Read Replies') }}
{% if replies %}
-
+
{{ gettext("You have received a reply. To protect your identity in the unlikely event someone learns your codename, please delete all replies when you're done with them. This also lets us know that you are aware of our reply. You can respond by submitting new files and messages above.") }}
-
+
{% for reply in replies %}
-
- {{ reply.date|rel_datetime_format(relative=True) }}
+
+ {{ reply.date|rel_datetime_format(relative=True) }}
@@ -96,16 +85,16 @@ Delete reply from {{ reply.d
{% endfor %}
{% else %}
- {{ gettext('No Messages') }}
+ {{ gettext('No Messages') }}
{% endif %}
diff --git a/securedrop/tests/functional/source_navigation_steps.py b/securedrop/tests/functional/source_navigation_steps.py
index 64b2459fcc..ebf33bd6db 100644
--- a/securedrop/tests/functional/source_navigation_steps.py
+++ b/securedrop/tests/functional/source_navigation_steps.py
@@ -58,25 +58,27 @@ def _source_chooses_to_submit_documents(self):
self.source_name = codename.text
def _source_shows_codename(self, verify_source_name=True):
- content = self.driver.find_element_by_id("codename-hint-content")
- assert not content.is_displayed()
+ # The DETAILS element will be missing the OPEN attribute if it is
+ # closed, hiding its contents.
+ content = self.driver.find_element_by_css_selector("details#codename-hint")
+ assert content.get_attribute("open") is None
- self.safe_click_by_id("codename-hint-show")
+ self.safe_click_by_id("codename-hint")
- self.wait_for(lambda: content.is_displayed())
- assert content.is_displayed()
- content_content = self.driver.find_element_by_css_selector("#codename-hint-content output")
+ assert content.get_attribute("open") is not None
+ content_content = self.driver.find_element_by_css_selector("details#codename-hint mark")
if verify_source_name:
assert content_content.text == self.source_name
def _source_hides_codename(self):
- content = self.driver.find_element_by_id("codename-hint-content")
- assert content.is_displayed()
+ # The DETAILS element will have the OPEN attribute if it is open,
+ # displaying its contents.
+ content = self.driver.find_element_by_css_selector("details#codename-hint")
+ assert content.get_attribute("open") is not None
- self.safe_click_by_id("codename-hint-hide")
+ self.safe_click_by_id("codename-hint")
- self.wait_for(lambda: not content.is_displayed())
- assert not content.is_displayed()
+ assert content.get_attribute("open") is None
def _source_sees_no_codename(self):
codename = self.driver.find_elements_by_css_selector(".code-reminder")
diff --git a/securedrop/tests/functional/test_source.py b/securedrop/tests/functional/test_source.py
index d449e4326c..a1f2387163 100644
--- a/securedrop/tests/functional/test_source.py
+++ b/securedrop/tests/functional/test_source.py
@@ -43,26 +43,26 @@ def test_lookup_codename_hint(self):
class TestDownloadKey(
- functional_test.FunctionalTest,
- journalist_navigation_steps.JournalistNavigationStepsMixin,
-):
+ functional_test.FunctionalTest,
+ journalist_navigation_steps.JournalistNavigationStepsMixin):
+
def test_journalist_key_from_source_interface(self):
- data = self.return_downloaded_content(
- self.source_location + "/public-key", None
- )
+ data = self.return_downloaded_content(self.source_location +
+ "/public-key", None)
- data = data.decode("utf-8")
+ data = data.decode('utf-8')
assert "BEGIN PGP PUBLIC KEY BLOCK" in data
class TestDuplicateSourceInterface(
- functional_test.FunctionalTest, source_navigation_steps.SourceNavigationStepsMixin
-):
+ functional_test.FunctionalTest,
+ source_navigation_steps.SourceNavigationStepsMixin):
+
def get_codename_generate(self):
return self.driver.find_element_by_css_selector("#codename").text
def get_codename_lookup(self):
- return self.driver.find_element_by_css_selector("#codename-hint-content output").text
+ return self.driver.find_element_by_css_selector("#codename-hint mark").text
def test_duplicate_generate_pages(self):
# Test generation of multiple codenames in different browser tabs, ref. issue 4458.
diff --git a/securedrop/tests/test_source.py b/securedrop/tests/test_source.py
index fd6e8dfb3f..588535067d 100644
--- a/securedrop/tests/test_source.py
+++ b/securedrop/tests/test_source.py
@@ -101,8 +101,8 @@ def _find_codename(html):
"""Find a source codename (diceware passphrase) in HTML"""
# Codenames may contain HTML escape characters, and the wordlist
# contains various symbols.
- codename_re = (r']*id="codename"[^>]*>'
- r'(?P[a-z0-9 ?:=@_.*+()\'"$%!-]+) ')
+ codename_re = (r']*id="codename"[^>]*>'
+ r'(?P[a-z0-9 ?:=@_.*+()\'"$%!-]+) ')
codename_match = re.search(codename_re, html)
assert codename_match is not None
return codename_match.group('codename')