Repository: freedomofpress/securedrop

Issue:
check that form errors are semantically linked to their triggering inputs (WCAG 3.3.1)
This ticket tracks forms in the Source and Journalist Interfaces and how their validation and error states are conveyed to the user.

| Interface | Template | Form | Form-level errors | Field-level validation |
| --- | --- | --- | --- | --- |
| Source | `login.html` | `LoginForm` | `flashed.html` | `aria-describedby="login-with-existing-code-name-errors"` |
| Source | `lookup.html` | `SubmissionForm` | `flashed.html` | Not possible after <https://github.com/freedomofpress/securedrop/blob/3810e859d7bb83741f11f18369a4b3e2da50b4b9/securedrop/source_app/main.py#L165> |
| Journalist | `account_edit_hotp_secret.html` | Inline | `flashed.html` | Not possible outside of WTForms without refactoring |
| Journalist | `admin_add_user.html` | `NewUserForm` | `flashed.html` | `aria-describedby` attributes |
| Journalist | `admin_edit_hotp_secret.html` | Inline | `flashed.html` | Not possible outside of WTForms without refactoring |
| Journalist | `admin_new_user_two_factor.html` | Inline | `flashed.html` | Not possible outside of WTForms without refactoring |
| Journalist | `admin.html` | Inline, action-oriented | Not applicable | Not applicable |
| Journalist | `col.html` | Inline, action-oriented | `flashed.html` | Not applicable |
| Journalist | `config.html` | `OrgNameForm` | `preferences_saved_flash.html` | Not possible after <https://github.com/freedomofpress/securedrop/blob/3810e859d7bb83741f11f18369a4b3e2da50b4b9/securedrop/journalist_app/admin.py#L105> |
| Journalist | `config.html` | `LogoForm` | `preferences_saved_flash.html` | Not possible after <https://github.com/freedomofpress/securedrop/blob/3810e859d7bb83741f11f18369a4b3e2da50b4b9/securedrop/journalist_app/admin.py#L62> |
| Journalist | `config.html` | `SubmissionPreferencesForm` | `preferences_saved_flash.html` | Not possible after <https://github.com/freedomofpress/securedrop/blob/3810e859d7bb83741f11f18369a4b3e2da50b4b9/securedrop/journalist_app/admin.py#L87> |
| Journalist | `config.html` | "Alerts": inline, action-oriented | `preferences_saved_flash.html` | Not applicable |
| Journalist | `delete.html` | Inline, action-oriented | `preferences_saved_flash.html` | Not applicable |
| Journalist | `edit_account.html` | Inline (multiple) | `flashed.html` | Not possible outside of WTForms without refactoring |
| Journalist | `index.html` | Inline, action-oriented | `flashed.html` | Not applicable |
| Journalist | `login.html` | Inline | `flashed.html` | Undesirable for security reasons |


Discussion:

Resolving patch:
diff --git a/securedrop/journalist_app/__init__.py b/securedrop/journalist_app/__init__.py
--- a/securedrop/journalist_app/__init__.py
+++ b/securedrop/journalist_app/__init__.py
@@ -105,6 +105,7 @@ def _handle_http_exception(
     app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat
     app.jinja_env.filters['html_datetime_format'] = \
         template_filters.html_datetime_format
+    app.jinja_env.add_extension('jinja2.ext.do')
 
     @app.before_first_request
     def expire_blacklisted_tokens() -> None:
diff --git a/securedrop/source_app/__init__.py b/securedrop/source_app/__init__.py
--- a/securedrop/source_app/__init__.py
+++ b/securedrop/source_app/__init__.py
@@ -85,6 +85,7 @@ def handle_csrf_error(e: CSRFError) -> werkzeug.Response:
     app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat
     app.jinja_env.filters['html_datetime_format'] = \
         template_filters.html_datetime_format
+    app.jinja_env.add_extension('jinja2.ext.do')
 
     for module in [main, info, api]:
         app.register_blueprint(module.make_blueprint(config))  # type: ignore


Test patch: