Repository: freedomofpress/securedrop

Issue:
Remove JS minification from the web applications.
## Description

In order to improve performance of the web applications, support for minification for JS and CSS files was added in https://github.com/freedomofpress/securedrop/commit/e74b39075fa4d407bf929ab8bc5ab24abad79f70, using `flask-assets`, `cssmin`, and `jsmin`.

Since then the application has started using SASS for CSS minification (see #2808), meaning only JS is being minified. Only 2 files are currently being minified, `static/js/{source,journalist}.js`, with savings of <1k and <2k respectively. 

To reduce code complexity at the cost of 1-2k in page size, it would make sense to drop JS minification as well. Alternatively, the files could be minified during the build process (tho IMO it's overkill given the minimal gains).

One other disadvantage of server-side minification is that the Apache user needs to be able to write the minified files somewhere on the webroot. Removing it would allow for tightening up the relevant apparmor profile, removing the related write perms

## User Research Evidence
n/a

## User Stories
n/a



Discussion:
Once you factor in gzip, the benefit of minification becomes pretty negligible. For source.js on 2.3.1:
```
$ du -b securedrop/static/js/source*.js* | sort
1167	securedrop/static/js/source.min.js.gz
1578	securedrop/static/js/source.js.gz
3271	securedrop/static/js/source.min.js
4501	securedrop/static/js/source.js
```

So +1 to getting rid of this extra complexity. The jsmin package is also unmaintained (c.f. https://github.com/tikitu/jsmin/issues/33#issuecomment-914040205) and problematic because the version we're on uses a now-removed setuptools option, which I had to workaround in https://github.com/freedomofpress/securedrop/issues/6358#issuecomment-1076844034.

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
@@ -5,7 +5,6 @@
 
 from flask import (Flask, session, redirect, url_for, flash, g, request,
                    render_template, json)
-from flask_assets import Environment
 from flask_babel import gettext
 from flask_wtf.csrf import CSRFProtect, CSRFError
 from os import path
@@ -64,7 +63,6 @@ def create_app(config: 'SDConfig') -> Flask:
     app.session_interface = JournalistInterfaceSessionInterface()
 
     csrf = CSRFProtect(app)
-    Environment(app)
 
     app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
     app.config['SQLALCHEMY_DATABASE_URI'] = config.DATABASE_URI
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
@@ -6,7 +6,6 @@
 import werkzeug
 from flask import (Flask, render_template, request, g, session, redirect, url_for)
 from flask_babel import gettext
-from flask_assets import Environment
 from flask_wtf.csrf import CSRFProtect, CSRFError
 from os import path
 from typing import Tuple
@@ -70,9 +69,6 @@ def setup_i18n() -> None:
     def handle_csrf_error(e: CSRFError) -> werkzeug.Response:
         return clear_session_and_redirect_to_logged_out_page(flask_session=session)
 
-    assets = Environment(app)
-    app.config['assets'] = assets
-
     app.jinja_env.trim_blocks = True
     app.jinja_env.lstrip_blocks = True
     app.jinja_env.globals['version'] = version.__version__


Test patch:
diff --git a/molecule/builder-focal/tests/test_securedrop_deb_package.py b/molecule/builder-focal/tests/test_securedrop_deb_package.py
--- a/molecule/builder-focal/tests/test_securedrop_deb_package.py
+++ b/molecule/builder-focal/tests/test_securedrop_deb_package.py
@@ -266,37 +266,25 @@ def test_securedrop_app_code_contains_mo_files(
 def test_deb_package_contains_no_generated_assets(securedrop_app_code_contents: str):
     """
     Ensures the `securedrop-app-code` package does not ship minified
-    static assets, which are built automatically via Flask-Assets, and
-    may be present in the source directory used to build from.
+    static assets previously built at runtime via Flask-Assets under /gen, and
+    which may be present in the source directory used to build from.
     """
-    # static/gen/ directory should exist
-    assert re.search(
-        r"^.*\./var/www/securedrop" "/static/gen/$", securedrop_app_code_contents, re.M
-    )
-    # static/gen/ directory should be empty
+    # static/gen/ directory not should exist
     assert not re.search(
-        r"^.*\./var/www/securedrop" "/static/gen/.+$",
-        securedrop_app_code_contents,
-        re.M,
+        r"^.*\./var/www/securedrop" "/static/gen/$", securedrop_app_code_contents, re.M
     )
 
-    # static/.webassets-cache/ directory should exist
-    assert re.search(
-        r"^.*\./var/www/securedrop" "/static/.webassets-cache/$",
-        securedrop_app_code_contents,
-        re.M,
-    )
-    # static/.webassets-cache/ directory should be empty
+    # static/.webassets-cache/ directory should not exist
     assert not re.search(
-        r"^.*\./var/www/securedrop" "/static/.webassets-cache/.+$",
+        r"^.*\./var/www/securedrop" "/static/.webassets-cache/$",
         securedrop_app_code_contents,
         re.M,
     )
 
-    # no SASS files should exist; only the generated CSS files.
+    # no SASS files should exist.
     assert not re.search("^.*sass$", securedrop_app_code_contents, re.M)
 
-    # no .map files should exist; only the generated CSS files.
+    # no .map files should exist.
     assert not re.search("^.*css.map$", securedrop_app_code_contents, re.M)