internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: cal-itp/benefits

Issue:
Send Content-Security-Policy header
The Content-Security-Policy (CSP) header (with the `frame-ancestors` directive) replaces the now deprecated `X-Frame-Options` header, to instruct the browser about appropriate actions to perform if a site is included inside an `<iframe>`.

See more at https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

We already have Django's built-in [Clickjacking/`X-Frame-Options` features enabled](https://github.com/cal-itp/benefits/blob/dev/benefits/settings.py#L52). Since this app should never be run from an `<iframe>`, let's create another Middleware that sets the CSP header like so:

```
Content-Security-Policy: default-src 'self'; frame-ancestors 'none';
```


Discussion:
Mozilla has helpfully implemented a Middleware that we can use for this!

https://github.com/mozilla/django-csp

https://django-csp.readthedocs.io/en/latest/

Resolving patch:
diff --git a/benefits/settings.py b/benefits/settings.py
--- a/benefits/settings.py
+++ b/benefits/settings.py
@@ -50,6 +50,7 @@
     "django.middleware.common.CommonMiddleware",
     "django.middleware.csrf.CsrfViewMiddleware",
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
+    "csp.middleware.CSPMiddleware",
     "benefits.core.middleware.DebugSession",
     "benefits.core.middleware.ChangedLanguageEvent",
 ]
@@ -188,3 +189,35 @@
 # Analytics configuration
 
 ANALYTICS_KEY = os.environ.get("ANALYTICS_KEY")
+
+# Content Security Policy
+# Configuration docs at https://django-csp.readthedocs.io/en/latest/configuration.html
+
+# In particular, note that the inner single-quotes are required!
+# https://django-csp.readthedocs.io/en/latest/configuration.html#policy-settings
+
+CSP_DEFAULT_SRC = ["'self'"]
+
+CSP_CONNECT_SRC = ["'self'", "https://api.amplitude.com/"]
+
+CSP_FONT_SRC = ["https://california.azureedge.net/cdt/statetemplate/", "https://fonts.gstatic.com/"]
+
+CSP_FRAME_ANCESTORS = ["'none'"]
+CSP_FRAME_SRC = ["'none'"]
+
+CSP_SCRIPT_SRC_ELEM = [
+    "'unsafe-inline'",
+    "https://california.azureedge.net/cdt/statetemplate/",
+    "https://cdn.amplitude.com/libs/",
+    "https://code.jquery.com/",
+    "*.littlepay.com",
+]
+
+CSP_STYLE_SRC = ["'unsafe-inline'"]
+
+CSP_STYLE_SRC_ELEM = [
+    "'self'",
+    "'unsafe-inline'",
+    "https://california.azureedge.net/cdt/statetemplate/",
+    "https://fonts.googleapis.com/css",
+]


Test patch:
Repository: cal-itp/benefits

Issue:
Send X-XSS-Protection header
The X-XSS-Protection header can be used to manage certain browser's protection against reflected cross-site scripting (XSS), stopping a page from being loaded if an attack is detected. In modern browsers, the Content-Security-Policy header can provide better protection against XSS and setting X-XSS-Protection might be redundant (#203 tracks CSP implementation).

See more at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection


We'll want the following header/value:

```
X-XSS-Protection: 1; mode=block
```

This can be done in a new Middleware and configured in [`settings.py`](https://github.com/cal-itp/benefits/blob/dev/benefits/settings.py#L45) for all requests/responses.


Discussion:

Resolving patch:
diff --git a/benefits/settings.py b/benefits/settings.py
--- a/benefits/settings.py
+++ b/benefits/settings.py
@@ -75,6 +75,8 @@
     CSRF_FAILURE_VIEW = "benefits.core.views.csrf_failure"
     SESSION_COOKIE_SECURE = True
 
+SECURE_BROWSER_XSS_FILTER = True
+
 ROOT_URLCONF = "benefits.urls"
 
 template_ctx_processors = [


Test patch:
Repository: cal-itp/benefits

Issue:
use `rel="noopener noreferrer"` for external links
1. GitHub link
2. Privacy Policy link
3. Transit agency link

<img width="974" alt="image" src="https://user-images.githubusercontent.com/3673236/142677414-95011bb6-2aaf-4136-a7e3-03cfc27f40f0.png">



Discussion:

Resolving patch:
diff --git a/benefits/core/viewmodels.py b/benefits/core/viewmodels.py
--- a/benefits/core/viewmodels.py
+++ b/benefits/core/viewmodels.py
@@ -16,6 +16,8 @@ class Button:
     * classes: str, str[]
     * text: str
     * url: str
+    * target: str
+    * rel: str
     """
 
     def __init__(self, **kwargs):
@@ -30,13 +32,14 @@ def __init__(self, **kwargs):
         self.text = kwargs.get("text", "Button")
         self.url = kwargs.get("url")
         self.target = kwargs.get("target")
+        self.rel = kwargs.get("rel")
 
     @staticmethod
     def agency_contact_links(agency):
         """Create link buttons for agency contact information."""
         return [
             # fmt: off
-            Button.link(classes="agency-url", label=agency.long_name, text=agency.info_url, url=agency.info_url, target="_blank"),  # noqa: E501
+            Button.link(classes="agency-url", label=agency.long_name, text=agency.info_url, url=agency.info_url, target="_blank", rel="noopener noreferrer"),  # noqa: E501
             Button.link(classes="agency-phone", text=agency.phone, url=f"tel:{agency.phone}"),
             # fmt: on
         ]


Test patch:
Repository: cal-itp/benefits

Issue:
Better HTTP response code handling in Eligibility API
In the Eligibility API `Client`, we currently attempt to parse the response as if it were a JWT, regardless of response code:

From https://github.com/cal-itp/benefits/blob/dev/benefits/eligibility/api.py#L145

```python
try:
    r = requests.get(self.verifier.api_url, headers=self._auth_headers(token))
except requests.ConnectionError:
    raise ApiError("Connection to verification server failed")
except requests.Timeout:
    raise ApiError("Connection to verification server timed out")
except requests.TooManyRedirects:
    raise ApiError("Too many redirects to verification server")
except requests.HTTPError as e:
    raise ApiError(e)

return self._tokenize_response(r)
```

Since input errors on the form are returned as JWTs, the same as success payloads, this code worked fine for 200 and 400 responses. But if the API outright rejects the call with a 403, the above code attempts to parse _that_ response as a JWT, throwing an unhandled exception.

Let's guard the `return self._tokenize_response(r)` to ensure we are only trying to tokenize the expected 200 and 400 responses; other codes should raise an `ApiError`.


Discussion:
I originally created this issue in `benefits`, but transferring over here as ultimately the fix will be in this repository.

Resolving patch:
diff --git a/benefits/eligibility/api.py b/benefits/eligibility/api.py
--- a/benefits/eligibility/api.py
+++ b/benefits/eligibility/api.py
@@ -154,8 +154,13 @@ def _request(self, sub, name):
         except requests.HTTPError as e:
             raise ApiError(e)
 
-        logger.debug("Process eligiblity verification response")
-        return self._tokenize_response(r)
+        expected_status_codes = {200, 400}
+        if r.status_code in expected_status_codes:
+            logger.debug("Process eligiblity verification response")
+            return self._tokenize_response(r)
+        else:
+            logger.warning(f"Unexpected eligibility verification response status code: {r.status_code}")
+            raise ApiError("Unexpected eligibility verification response")
 
     def verify(self, sub, name):
         """Check eligibility for the subject and name."""


Test patch:
Repository: cal-itp/benefits

Issue:
Create VerifierSessionRequired middleware that expects a verifier to exist in session
## Background

Once the session tracks the selected verifier in #321, we can make use of that infrastructure to put guards on certain view functions that require a verifier to be selected. The first step is to create a new middleware class that enforces the requirement.

This is similar to how the [`AgencySessionRequired`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/middleware.py#L20) and [`EligibileSessionRequired`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/middleware.py#L68) middleware are used.

## Tasks

- [x] Create a new middleware class like `VerifierSessionRequired` inheriting from `MiddlewareMixin`, see the other `*SessionRequired` as examples
- [x] In `process_request()`, check `session.verifier()` for the request.
  - If `None`, raise an error to stop the request
  - Otherwise return `None` to allow the request to continue
- [x] Apply this middleware to the following views to enforce that a verifier is needed:
  - [x]  [`eligibility:index`](https://github.com/cal-itp/benefits/blob/dev/benefits/eligibility/views.py#L16)
  - [x]  [`eligibility:confirm`](https://github.com/cal-itp/benefits/blob/dev/benefits/eligibility/views.py#L46)



Discussion:

Resolving patch:
diff --git a/benefits/core/middleware.py b/benefits/core/middleware.py
--- a/benefits/core/middleware.py
+++ b/benefits/core/middleware.py
@@ -96,6 +96,17 @@ def __call__(self, request):
         return self.get_response(request)
 
 
+class VerifierSessionRequired(MiddlewareMixin):
+    """Middleware raises an exception for sessions lacking an eligibility verifier configuration."""
+
+    def process_request(self, request):
+        if session.verifier(request):
+            logger.debug("Session configured with eligibility verifier")
+            return None
+        else:
+            raise AttributeError("Session not configured with eligibility verifier")
+
+
 class ViewedPageEvent(MiddlewareMixin):
     """Middleware sends an analytics event for page views."""
 
diff --git a/benefits/eligibility/views.py b/benefits/eligibility/views.py
--- a/benefits/eligibility/views.py
+++ b/benefits/eligibility/views.py
@@ -13,6 +13,7 @@
 
 
 @decorator_from_middleware(middleware.AgencySessionRequired)
+@decorator_from_middleware(middleware.VerifierSessionRequired)
 def index(request):
     """View handler for the eligibility verification getting started screen."""
 
@@ -43,6 +44,7 @@ def index(request):
 
 @decorator_from_middleware(middleware.AgencySessionRequired)
 @decorator_from_middleware(middleware.RateLimit)
+@decorator_from_middleware(middleware.VerifierSessionRequired)
 def confirm(request):
     """View handler for the eligibility verification form."""
 


Test patch:
Repository: cal-itp/benefits

Issue:
"Unverified" page should use translation fields on EligibilityVerifier
### To reproduce
Go through the eligibility verification flow such that you will see the "unverified" page that an ineligible user would see.

### Expected result
The page shows with translated content.

### Actual result
The page shows with translation strings.

![image](https://user-images.githubusercontent.com/25497886/160946133-28ff21fa-9b78-475f-81c5-2af69fc7beaa.png)


### Thoughts
I think we forgot to update the `unverified` function in the eligibility app's views when we did #338.


Discussion:

Resolving patch:
diff --git a/benefits/eligibility/views.py b/benefits/eligibility/views.py
--- a/benefits/eligibility/views.py
+++ b/benefits/eligibility/views.py
@@ -156,6 +156,7 @@ def verified(request, verified_types):
 
 
 @decorator_from_middleware(middleware.AgencySessionRequired)
+@decorator_from_middleware(middleware.VerifierSessionRequired)
 def unverified(request):
     """View handler for the unverified eligibility page."""
 
@@ -165,11 +166,13 @@ def unverified(request):
     agency = session.agency(request)
     buttons = viewmodels.Button.agency_contact_links(agency)
 
+    verifier = session.verifier(request)
+
     page = viewmodels.Page(
-        title=_("eligibility.pages.unverified.title"),
-        content_title=_("eligibility.pages.unverified.content_title"),
+        title=_(verifier.unverified_title),
+        content_title=_(verifier.unverified_content_title),
         icon=viewmodels.Icon("idcardquestion", pgettext("image alt text", "core.icons.idcardquestion")),
-        paragraphs=[_("eligibility.pages.unverified.p[0]"), _("eligibility.pages.unverified.p[1]")],
+        paragraphs=[_(verifier.unverified_blurb), _("eligibility.pages.unverified.p[1]")],
         buttons=buttons,
         classes="text-lg-center",
     )


Test patch:
Repository: cal-itp/benefits

Issue:
Create a specific `eligibility/start.html` template, update the view to use it
Like for some other pages, we need a specific template file for the Eligibility Start page, which has some unique elements to render and behaviors to handle.

The scope of this task it to simply create the new template, `{% extends "core/page.html" %}`, and get the view to use it -- keeping everything else about the page the same.


Discussion:

Resolving patch:
diff --git a/benefits/eligibility/views.py b/benefits/eligibility/views.py
--- a/benefits/eligibility/views.py
+++ b/benefits/eligibility/views.py
@@ -3,6 +3,7 @@
 """
 from django.contrib import messages
 from django.shortcuts import redirect
+from django.template.response import TemplateResponse
 from django.urls import reverse
 from django.utils.decorators import decorator_from_middleware
 from django.utils.translation import pgettext, gettext as _
@@ -79,7 +80,7 @@ def start(request):
         button=viewmodels.Button.primary(text=_("eligibility.buttons.continue"), url=reverse("eligibility:confirm")),
     )
 
-    return PageTemplateResponse(request, page)
+    return TemplateResponse(request, "eligibility/start.html", page.context_dict())
 
 
 @decorator_from_middleware(middleware.AgencySessionRequired)


Test patch:
Repository: cal-itp/benefits

Issue:
Add logging for OAuth flows
We merged the basic implementation in #414, but neglected to include any additional logging around the new flows/logic.

Some ideas of what we should log:

- [x] The `OAUTH_CLIENT_NAME` used
- [x] The `redirect_uri` sent to the authorization server with the `authorize_redirect` request
- [x] If an access token fails to be authorized


Discussion:

Resolving patch:
diff --git a/benefits/oauth/views.py b/benefits/oauth/views.py
--- a/benefits/oauth/views.py
+++ b/benefits/oauth/views.py
@@ -1,3 +1,5 @@
+import logging
+
 from django.shortcuts import redirect
 from django.urls import reverse
 
@@ -7,7 +9,12 @@
 from benefits.settings import OAUTH_CLIENT_NAME
 
 
+logger = logging.getLogger(__name__)
+
+
 if OAUTH_CLIENT_NAME:
+    logger.debug(f"Using OAuth client configuration: {OAUTH_CLIENT_NAME}")
+
     _oauth = OAuth()
     _oauth.register(OAUTH_CLIENT_NAME)
     oauth_client = _oauth.create_client(OAUTH_CLIENT_NAME)
@@ -25,6 +32,8 @@ def login(request):
     route = reverse(ROUTE_AUTH)
     redirect_uri = request.build_absolute_uri(route)
 
+    logger.debug(f"OAuth authorize_redirect with redirect_uri: {redirect_uri}")
+
     return oauth_client.authorize_redirect(request, redirect_uri)
 
 
@@ -32,11 +41,14 @@ def authorize(request):
     if not oauth_client:
         raise Exception("No OAuth client")
 
+    logger.debug("Attempting to authorize OAuth access token")
     token = oauth_client.authorize_access_token(request)
 
     if token is None:
+        logger.warning("Could not authorize OAuth access token")
         return redirect(ROUTE_START)
     else:
         # we are intentionally not storing anything about the user, including their token
+        logger.debug("OAuth access token authorized")
         session.update(request, auth=True)
         return redirect(ROUTE_CONFIRM)


Test patch:
Repository: cal-itp/benefits

Issue:
Ensure OAuth redirect URLs are secure
We construct the OAUth redirect URLs using the Django [`reverse()`](https://docs.djangoproject.com/en/3.2/ref/urlresolvers/#reverse) helper and [`request.build_absolute_uri()`](https://docs.djangoproject.com/en/3.2/ref/request-response/#django.http.HttpRequest.build_absolute_uri).

This is done in e.g. the [`login()` view](https://github.com/cal-itp/benefits/blob/dev/benefits/oauth/views.py):

```python
ROUTE_AUTH = "oauth:authorize"

route = reverse(ROUTE_AUTH)

redirect_uri = request.build_absolute_uri(route)
```

The docs for `build_absolute_uri` say:

> Mixing HTTP and HTTPS on the same site is discouraged, therefore [build_absolute_uri()](https://docs.djangoproject.com/en/3.2/ref/request-response/#django.http.HttpRequest.build_absolute_uri) will always generate an absolute URI with the same scheme the current request has. If you need to redirect users to HTTPS, it’s best to let your Web server redirect all HTTP traffic to HTTPS.

When running in AWS, we have the Load Balancer sitting in front of all requests; this is the layer where HTTP --> HTTPS redirection happens and where the cert that the browser sees is installed.

Then the request gets to the application container, where it first hits `nginx`. This is where requests for static files end; application requests are forwarded through to `gunicorn` over a Unix socket. Strictly speaking, Django does not receive an HTTPS request.

## Potential solution

The [`SECURE_PROXY_SSL_HEADER`](https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header) Django setting seems to be aimed at helping this situation, where `nginx` sets a header with an indicated value on the request forwarded into the application via gunicorn, to tell the app if the request was originally secure or not.

In the app's [`nginx.conf`](https://github.com/cal-itp/benefits/blob/dev/nginx.conf#L55) we do set that header on forwarded requests.

## Todo

* [x] Ensure the current `nginx.conf` setting overwrites any header value coming in with the request and/or clear that header first
* [x] Add the `SECURE_PROXY_SSL_HEADER` to `settings.py` when `DEBUG = False`


Discussion:

Resolving patch:
diff --git a/benefits/settings.py b/benefits/settings.py
--- a/benefits/settings.py
+++ b/benefits/settings.py
@@ -90,6 +90,14 @@ def _filter_empty(ls):
 
 SECURE_BROWSER_XSS_FILTER = True
 
+# the NGINX reverse proxy sits in front of the application in deployed environments
+# SSL terminates before getting to Django, and NGINX adds this header to indicate
+# if the original request was secure or not
+#
+# See https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header
+if not DEBUG:
+    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
+
 ROOT_URLCONF = "benefits.urls"
 
 template_ctx_processors = [


Test patch:
Repository: cal-itp/benefits

Issue:
Remove all paragraph content from the viewmodel in `core:index`
From https://github.com/cal-itp/benefits/issues/366
<img width="832" alt="image" src="https://user-images.githubusercontent.com/3673236/163299119-7b8c7696-05b5-4ade-9bc2-c5b784707a66.png">




Discussion:
@MattRenda Are you currently working on this? If you are busy with other work, feel free to put it back for someone else to potentially pick up
Talked with @machikoyasuda and @thekaveman, and one of us will pick this up so we can get the change in today.

Resolving patch:
diff --git a/benefits/core/views.py b/benefits/core/views.py
--- a/benefits/core/views.py
+++ b/benefits/core/views.py
@@ -20,11 +20,6 @@ def _index_content_title():
     return _("core.pages.index.content_title")
 
 
-def _index_paragraphs():
-    """Helper returns the content paragraphs for the common index page."""
-    return [_("core.pages.index.p[0]"), _("core.pages.index.p[1]"), _("core.pages.index.p[2]")]
-
-
 def _index_url():
     """Helper computes the index url path."""
     return reverse("core:index")
@@ -43,7 +38,6 @@ def index(request):
 
     page = viewmodels.Page(
         content_title=_index_content_title(),
-        paragraphs=_index_paragraphs(),
         buttons=buttons,
         classes="home",
     )


Test patch:
Repository: cal-itp/benefits

Issue:
Fix styles on Help page: agency links and font sizes
We got feedback from @srhhnry and @Indiajar in our meeting on 2022-04-15. This ticket is for:
 - Transit agency links should match Figma mockup

Also, the Help page was implemented in #403 assuming the styles from #397 took care of the font sizing, letter spacing, etc. As shown on #444 for the agency index page, this was not the case, so we should go back through and compare the current implementation against the Figma mockup and make appropriate fixes.


Discussion:

Resolving patch:
diff --git a/benefits/core/viewmodels.py b/benefits/core/viewmodels.py
--- a/benefits/core/viewmodels.py
+++ b/benefits/core/viewmodels.py
@@ -40,10 +40,10 @@ def __init__(self, **kwargs):
     def agency_contact_links(agency):
         """Create link buttons for agency contact information."""
         return [
-            # fmt: off
-            Button.link(classes="agency-url", label=agency.long_name, text=agency.info_url, url=agency.info_url, target="_blank", rel="noopener noreferrer"),  # noqa: E501
-            Button.link(classes="agency-phone", text=agency.phone, url=f"tel:{agency.phone}"),
-            # fmt: on
+            Button.link(classes="agency", label=agency.long_name, text=agency.phone, url=f"tel:{agency.phone}"),
+            Button.link(
+                classes="agency", text=agency.info_url, url=agency.info_url, target="_blank", rel="noopener noreferrer"
+            ),
         ]
 
     @staticmethod
diff --git a/benefits/core/views.py b/benefits/core/views.py
--- a/benefits/core/views.py
+++ b/benefits/core/views.py
@@ -82,7 +82,6 @@ def help(request):
         title=_("core.buttons.help"),
         content_title=_("core.buttons.help"),
         buttons=buttons,
-        classes="text-lg-center",
         noimage=True,
     )
 
diff --git a/benefits/eligibility/views.py b/benefits/eligibility/views.py
--- a/benefits/eligibility/views.py
+++ b/benefits/eligibility/views.py
@@ -217,16 +217,17 @@ def unverified(request):
     # tel: link to agency phone number
     agency = session.agency(request)
     buttons = viewmodels.Button.agency_contact_links(agency)
+    buttons.append(viewmodels.Button.home(request, _("core.buttons.retry")))
 
     verifier = session.verifier(request)
 
     page = viewmodels.Page(
         title=_(verifier.unverified_title),
+        classes="with-agency-links",
         content_title=_(verifier.unverified_content_title),
         icon=viewmodels.Icon("idcardquestion", pgettext("image alt text", "core.icons.idcardquestion")),
         paragraphs=[_(verifier.unverified_blurb), _("eligibility.pages.unverified.p[1]")],
         buttons=buttons,
-        classes="text-lg-center",
     )
 
     return PageTemplateResponse(request, page)


Test patch:
Repository: cal-itp/benefits

Issue:
Mobile: New button design for Sign Out
<img width="386" alt="image" src="https://user-images.githubusercontent.com/3673236/166524695-c3e2bca8-7833-40a1-b463-888dd25c264f.png">



Discussion:

Resolving patch:
diff --git a/benefits/core/viewmodels.py b/benefits/core/viewmodels.py
--- a/benefits/core/viewmodels.py
+++ b/benefits/core/viewmodels.py
@@ -141,7 +141,6 @@ def __init__(self, **kwargs):
         if not isinstance(self.classes, list):
             self.classes = self.classes.split(" ")
         if not self.noimage:
-            self.image = "img/ridertappingbankcard.png"
             self.classes.append("with-image")
 
     def context_dict(self):


Test patch: