Compare commits

..

4 Commits

Author SHA1 Message Date
alexjanousekGSA
a962da9c7d Added existing csp header 2025-04-10 12:15:34 -04:00
alexjanousekGSA
3a468c77aa Fixed unit test 2025-04-10 08:20:47 -04:00
alexjanousekGSA
fff81870e5 Fixed header 2025-04-08 13:03:01 -04:00
alexjanousekGSA
b5675e586e Fixed security issue and noscript iframe 2025-04-07 14:45:28 -04:00
25 changed files with 225 additions and 102 deletions

4
.gitignore vendored
View File

@@ -14,10 +14,6 @@
*.XLSX
## Non user files allowed to be commited
!app/assets/pdf/best-practices-for-texting-the-public.pdf
!app/assets/pdf/investing-in-notifications-tts-public-benefits-studio-decision-memo.pdf
!app/assets/pdf/best-practices-section-outline.pdf
!app/assets/pdf/standing-up-your-own-notify.pdf
!app/assets/pdf/tcpa_overview.pdf
!app/assets/pdf/investing-notifications-tts-public-benefits-memo.pdf
!app/assets/pdf/out-of-pilot-announcement.pdf

View File

@@ -8,7 +8,6 @@ repos:
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
exclude: ^app/assets/pdf/.*\.pdf$
- id: debug-statements
- id: check-merge-conflict
- id: check-toml

View File

@@ -169,6 +169,11 @@ def _csp(config):
def create_app(application):
@application.after_request
def add_csp_header(response):
existing_csp = response.headers.get("Content-Security-Policy", "")
response.headers["Content-Security-Policy"] = existing_csp + "; form-action 'self';"
return response
# @application.context_processor
# def inject_feature_flags():
# this is where feature flags can be easily added as a dictionary within context

View File

@@ -0,0 +1,29 @@
(function(Modules) {
"use strict";
let isSixDigitHex = value => value.match(/^#[0-9A-F]{6}$/i);
let colourOrWhite = value => isSixDigitHex(value) ? value : '#FFFFFF';
Modules.ColourPreview = function() {
this.start = component => {
this.$input = $(component);
this.$input.closest('.usa-form-group').append(
this.$preview = $('<span class="textbox-colour-preview"></span>')
);
this.$input
.on('input', this.update)
.trigger('input');
};
this.update = () => this.$preview.css(
'background', colourOrWhite(this.$input.val())
);
};
})(window.GOVUK.Modules);

View File

@@ -0,0 +1,24 @@
(function(window) {
"use strict";
window.GOVUK.Modules.Homepage = function() {
this.start = function(component) {
let $component = $(component);
let iterations = 0;
let timeout = null;
$component.on('click', () => {
if (++iterations == 5) {
$component.toggleClass('product-page-intro-wrapper--alternative');
}
clearTimeout(timeout);
timeout = setTimeout(() => iterations = 0, 1500);
});
};
};
})(window);

View File

@@ -28,17 +28,8 @@ def get_csv_upload(service_id, upload_id):
return get_s3_object(*get_csv_location(service_id, upload_id))
def remove_blank_lines(filedata):
# sometimes people upload files with hundreds of blank lines at the end
data = filedata["data"]
cleaned_data = "\n".join(line for line in data.splitlines() if line.strip())
filedata["data"] = cleaned_data
return filedata
def s3upload(service_id, filedata):
filedata = remove_blank_lines(filedata)
upload_id = str(uuid.uuid4())
bucket_name, file_location, access_key, secret_key, region = get_csv_location(
service_id, upload_id

View File

@@ -14,7 +14,7 @@
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
{% block bodyStart %}
{% block extra_javascripts_before_body %}
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
<noscript><iframe sandbox src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
{% endblock %}
{% endblock %}

View File

@@ -16,10 +16,7 @@
<li>More effectively deliver program outcomes</li>
<li>Save administrative costs</li>
<li>Implement <a class="use-link usa-link--external"
href="https://digital.gov/resources/delivering-digital-first-public-experience/"
rel="noopener noreferrer"
target="_blank"
aria-label="21st Century IDEA (opens in a new tab)">21st Century
href="https://digital.gov/resources/delivering-digital-first-public-experience/" target="_blank">21st Century
IDEA</a> and other directives</li>
</ul>
<p>Notify.gov is an easy-to-use, web-based platform. It requires no technical expertise or system integration — users

View File

@@ -22,26 +22,16 @@
</ul>
<p>
Notify.gov operates under a full three-year <a class="use-link usa-link--external"
href="https://digital.gov/resources/an-introduction-to-ato/"
target="_blank"
rel="noopener noreferrer"
aria-label="Authority-to-Operate (ATO) (opens in a new tab)">Authority-to-Operate (ATO)</a>. This
href="https://digital.gov/resources/an-introduction-to-ato/" target="_blank">Authority-to-Operate (ATO)</a>. This
federal security authorization process leverages security
controls provided by National Institute of Standards and Technology (NIST).
</p>
<p>
Our infrastructure runs on <a class="use-link usa-link--external"
href="https://www.cloud.gov/"
target="_blank"
rel="noopener noreferrer"
aria-label="cloud dot gov (opens in a new tab)">cloud.gov</a> and utilizes several
Our infrastructure runs on <a class="use-link usa-link--external" href="https://www.cloud.gov/"
target="_blank">cloud.gov</a> and utilizes several
services through Amazon Web
Services (AWS), including <a class="use-link usa-link--external"
href="https://aws.amazon.com/sns/"
rel="noopener noreferrer"
target="_blank"
aria-label="AWS SNS (opens in a new tab)">
Services (AWS), including <a class="use-link usa-link--external" href="https://aws.amazon.com/sns/" target="_blank">
AWS SNS </a> for sending SMS
messages.
</p>
@@ -63,22 +53,15 @@
</p>
<h2>Multi-Factor Authentication</h2>
<p>
Notify.gov uses <a class="use-link usa-link--external"
href="https://login.gov/what-is-login/"
rel="noopener noreferrer"
target="_blank"
aria-label="Login dot gov (opens in a new tab)">Login.gov</a> for enhanced security.
Notify.gov uses <a class="use-link usa-link--external" href="https://login.gov/what-is-login/"
target="_blank">Login.gov</a> for enhanced security.
Login.gov is an extra layer of security created by the government that uses multi-factor authentication and stronger
passwords to protect your account.
</p>
<p>
To access Notify.gov, users will use a Login.gov account associated with their agency (.gov) email with one of the
<a class="use-link usa-link--external"
href="https://login.gov/help/get-started/authentication-methods/"
rel="noopener noreferrer"
target="_blank"
aria-label="login dot gov multi-factor authentication methods (opens in a new tab)"
>multi-factor authentication
<a class="use-link usa-link--external" href="https://login.gov/help/get-started/authentication-methods/"
target="_blank">multi-factor authentication
methods</a> offered through Login.gov.
</p>
</section>

View File

@@ -16,9 +16,7 @@
<p>
Confusing or <a class="use-link usa-link--external"
href="https://kffhealthnews.org/news/tougher-returned-mail-policies-add-to-medicaid-enrollment-drop/"
target="_blank"
rel="noopener noreferrer"
aria-label="unreceived notifications (opens in a new tab)">unreceived notifications</a> are one of the largest barriers to people getting and keeping
target="_blank">unreceived notifications</a> are one of the largest barriers to people getting and keeping
benefits. The typical ways the government communicates with people often fall short. Low income households are more
likely to experience housing instability, which means paper mail, already slow, can easily be missed.
</p>
@@ -26,9 +24,7 @@
<p>
<a class="use-link usa-link--external"
href="https://www.pewresearch.org/internet/fact-sheet/mobile/?tabItem=64e32376-5a21-4b1d-8f8b-5f92406db984"
target="_blank"
rel="noopener noreferrer"
aria-label="Pew Research shows that nearly all adults in the US have a cell phone (opens in a new tab)">Pew Research shows that nearly all adults in the US have a cell phone.</a> Reliance on smartphones
target="_blank">Pew Research shows that nearly all adults in the US have a cell phone.</a> Reliance on smartphones
for online access is especially common among Americans with lower household incomes and those with lower levels of
formal education. Of those earning less than $30,000 a year, 28% say their mobile phone is the sole method to
digitally connect.

View File

@@ -23,9 +23,7 @@
<p>
When the <a class="use-link usa-link--external"
href="https://www.cbpp.org/research/food-assistance/targeted-text-message-outreach-can-increase-wic-enrollment-pilots-show"
target="_blank"
rel="noopener noreferrer"
aria-label="Center on Budget and Policy Priorities (opens in a new tab)">Center on Budget and Policy Priorities</a> studied WIC, they found key learnings about the
target="_blank">Center on Budget and Policy Priorities</a> studied WIC, they found key learnings about the
quantity of messages delivered, how people engage with messages, and how they take action.
</p>
@@ -70,11 +68,8 @@
</li>
</ul>
<p>
The <a class="use-link usa-link--external"
href="https://codeforamerica.org/resources/texting-playbook/"
target="_blank"
rel="noopener noreferrer"
aria-label="Code for America's Texting Playbook (opens in a new tab)">Code for Americas Texting
The <a class="use-link usa-link--external" href="https://codeforamerica.org/resources/texting-playbook/"
target="_blank">Code for Americas Texting
Playbook</a>
reported specific learnings around appointment reminders, completing
document submission, and maintenance reminders.

View File

@@ -142,9 +142,7 @@
<p>
The Department of Veterans Affairs provides a <a class="use-link usa-link--external"
href="https://github.com/department-of-veterans-affairs/va.gov-team/blob/master/products/va-notify/notification-guide.md#vanotify-notification-guide"
target="_blank"
rel="noopener noreferrer"
aria-label="helpful flow-chart (opens in a new tab)">helpful flow-chart</a> that can help you decide if a text message is needed
target="_blank">helpful flow-chart</a> that can help you decide if a text message is needed
for the communication problem you are trying to solve.
</p>
<h2>Use a hypothesis framework to plan your campaign</h2>

View File

@@ -101,11 +101,8 @@
<p class="sms-message-wrapper text-left">
{{circle_number(7) }}<b>Dept. of Social Services:</b> Hi {{circle_number(8) }}<b>Julie,</b> Your Medicaid
renewal is closing December 31, 2023. You can renew online at {{circle_number(9) }}<a
class="use-link usa-link--external"
href="https://www.application.yourstate.gov"
target="_blank"
rel="application.yourstate.gov"
aria-label="Yourstate dot gov (opens in a new tab)">https://www.application.yourstate.gov</a> or {{circle_number(10) }}
class="use-link usa-link--external" href="https://www.application.yourstate.gov" target="_blank"
rel="application.yourstate.gov">https://www.application.yourstate.gov</a> or {{circle_number(10) }}
<b>call the number on the back</b> of your Medicaid card.
</p>
</div>

View File

@@ -29,10 +29,7 @@
{% set links = [
{
"p_text": 'The <a class="use-link usa-link--external" href="https://www.fcc.gov/sites/default/files/tcpa-rules.pdf"
target="_blank"
rel="noopener noreferrer"
aria-label="Telephone Consumer Protection Act (TCPA) (opens in a new tab)"
>Telephone Consumer
target="_blank">Telephone Consumer
Protection Act (TCPA)</a> (47 USC § 227) is the federal law that impacts how organizations are allowed to
communicate
in bulk with the public via telephone (including text message or SMS).'
@@ -40,9 +37,7 @@
{
"p_text": 'The <a class="use-link usa-link--external"
href="https://www.fcc.gov/document/fcc-provides-best-practices-enable-critical-health-care-coverage-calls"
target="_blank"
rel="noopener noreferrer"
aria-label="FCC has ruled (opens in a new tab)">FCC has ruled</a> that <b>Federal</b> and <b>State</b> programs are exempt from the TCPA and can
target="_blank">FCC has ruled</a> that <b>Federal</b> and <b>State</b> programs are exempt from the TCPA and can
send
text messages to the public without consent if conducting official business. Without explicit mention in the ruling,
local governments, phone carriers, or any texting intermediaries might require it.'
@@ -57,10 +52,7 @@
case.
For additional questions about the enforcement of the TCPA, you can watch <a class="use-link usa-link--external"
href="https://www.performance.gov/cx/life-experiences/facing-a-financial-shock/outputs/fcc-training/"
target="_blank"
rel="noopener noreferrer"
aria-label="a recorded training on public
benefits texting (opens in a new tab)">a recorded training on public
target="_blank">a recorded training on public
benefits texting</a> provided by the FCC.'
},
] %}
@@ -73,9 +65,7 @@
<h4 class="usa-collection__heading">
<a class="usa-link {% if item.is_downloadable %}usa-link--downloadable{% else %}usa-link--external{% endif %}"
href="{% if item.is_downloadable %}{{ asset_url(item.url_link) }}{% else %}{{ item.url_link }}{% endif %}"
target="_blank"
rel="noopener noreferrer"
aria-label="{{ item.url_text }} (opens in a new tab)">
target="_blank">
{{ item.url_text }}
</a>
</h4>
@@ -129,7 +119,6 @@
process
to keep your phone number list up-to-date. The FCC offers a <a class="use-link usa-link--external"
href="https://www.fcc.gov/reassigned-numbers-database" target="_blank"
aria-label="Reassigned Numbers Database (opens in a new tab)"
rel="Reassigned Numbers Database">Reassigned
Numbers Database</a> to identify phone numbers that may have been reassigned since your agency obtained them.
</p>

View File

@@ -81,10 +81,7 @@
</ul>
<h2>What provoking action looks like</h2>
<p class="padding-bottom-2"><a class="use-link usa-link--external"
href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC10002044/"
target="_blank"
rel="noopener noreferrer"
aria-label="Evidence shows (opens in a new tab)">Evidence
href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC10002044/" target="_blank">Evidence
shows</a> that employing
behavioral science is an effective way to increase the
likelihood of a recipient

View File

@@ -11,7 +11,7 @@
</h1>
<p>
<a class="usa-link" target="_blank" rel="noopener noreferrer" aria-label="Download live services CSV report (opens in a new tab)" href="{{ url_for('main.live_services_csv') }}">Download live services csv report</a>
<a class="usa-link" target="_blank" href="{{ url_for('main.live_services_csv') }}">Download live services csv report</a>
</p>
<p>
<a class="usa-link" href="{{ url_for('main.notifications_sent_by_service') }}">Monthly notification statuses for live services</a>

View File

@@ -68,11 +68,13 @@ const javascripts = () => {
paths.src + 'javascripts/errorTracking.js',
paths.src + 'javascripts/preventDuplicateFormSubmissions.js',
paths.src + 'javascripts/fullscreenTable.js',
paths.src + 'javascripts/colourPreview.js',
paths.src + 'javascripts/templateFolderForm.js',
paths.src + 'javascripts/collapsibleCheckboxes.js',
paths.src + 'javascripts/radioSlider.js',
paths.src + 'javascripts/updateStatus.js',
paths.src + 'javascripts/errorBanner.js',
paths.src + 'javascripts/homepage.js',
paths.src + 'javascripts/timeoutPopup.js',
paths.src + 'javascripts/date.js',
paths.src + 'javascripts/loginAlert.js',

View File

@@ -14,7 +14,7 @@ def test_owasp_useful_headers_set(
assert response.headers["X-Frame-Options"] == "deny"
assert response.headers["X-Content-Type-Options"] == "nosniff"
csp = response.headers["Content-Security-Policy"]
assert search(r"default-src 'self' static\.example\.com;", csp)
assert search(r"frame-src https://www.googletagmanager.com", csp)
assert search(r"frame-ancestors 'none';", csp)
assert search(r"form-action 'self';", csp)
assert search(

View File

@@ -1,6 +1,6 @@
from unittest.mock import Mock
from app.s3_client.s3_csv_client import remove_blank_lines, set_metadata_on_csv_upload
from app.s3_client.s3_csv_client import set_metadata_on_csv_upload
def test_sets_metadata(client_request, mocker):
@@ -21,11 +21,3 @@ def test_sets_metadata(client_request, mocker):
MetadataDirective="REPLACE",
ServerSideEncryption="AES256",
)
def test_removes_blank_lines():
filedata = {
"data": "phone number\r\n15555555555\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
}
file_data = remove_blank_lines(filedata)
assert file_data == {"data": "phone number\n15555555555"}

View File

@@ -18,12 +18,6 @@ Object.defineProperty(HTMLElement.prototype, 'clientWidth', {
});
// beforeAll hook to set up the DOM and load D3.js script
beforeAll(() => {
jest.spyOn(Intl, 'DateTimeFormat').mockImplementation(() => ({
resolvedOptions: () => ({ timeZone: 'UTC' })
}));
});
beforeAll(done => {
// Set up the DOM with the D3 script included
document.body.innerHTML = `

View File

@@ -0,0 +1,139 @@
const helpers = require('./support/helpers.js');
beforeAll(() => {
require('../../app/assets/javascripts/colourPreview.js');
});
afterAll(() => {
require('./support/teardown.js');
});
describe('Colour preview', () => {
let field;
let textbox;
let swatchEl;
beforeEach(() => {
// set up DOM
document.body.innerHTML = `
<div class="usa-form-group">
<label class="govuk-form-label" for="colour">
Colour
</label>
<input class="govuk-input govuk-input--width-6" id="colour" name="colour" rows="8" type="text" value="" data-module="colour-preview">
</div>`;
field = document.querySelector('.usa-form-group');
textbox = document.querySelector('input[type=text]');
});
afterEach(() => {
document.body.innerHTML = '';
});
describe("When the page loads", () => {
test("It should add a swatch element for the preview", () => {
// start the module
window.GOVUK.modules.start();
swatchEl = document.querySelector('.textbox-colour-preview');
expect(swatchEl).not.toBeNull();
});
test("If the textbox is empty it should make the swatch white", () => {
// start the module
window.GOVUK.modules.start();
swatchEl = document.querySelector('.textbox-colour-preview');
// textbox defaults to empty
// colours are output in RGB
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
});
test("If the textbox has a value which is a hex code it should add that colour to the swatch", () => {
textbox.setAttribute('value', '#00FF00');
// start the module
window.GOVUK.modules.start();
swatchEl = document.querySelector('.textbox-colour-preview');
// colours are output in RGB
expect(swatchEl.style.background).toEqual('rgb(0, 255, 0)');
});
test("If the textbox has a value which isn't a hex code it should make the swatch white", () => {
textbox.setAttribute('value', 'green');
// start the module
window.GOVUK.modules.start();
swatchEl = document.querySelector('.textbox-colour-preview');
// colours are output in RGB
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
});
});
describe("When input is added to the textbox", () => {
beforeEach(() => {
// start the module
window.GOVUK.modules.start();
swatchEl = document.querySelector('.textbox-colour-preview');
});
test("If the textbox is empty it should make the swatch white", () => {
helpers.triggerEvent(document.querySelector('input[type=text]'), 'input');
// textbox defaults to empty
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
});
test("If the textbox has a value which is a hex code it should add that colour to the swatch", () => {
textbox.setAttribute('value', '#00FF00');
helpers.triggerEvent(document.querySelector('input[type=text]'), 'input');
// textbox defaults to empty
expect(swatchEl.style.background).toEqual('rgb(0, 255, 0)');
});
test("If the textbox has a value which isn't a hex code it should make the swatch white", () => {
textbox.setAttribute('value', 'green');
helpers.triggerEvent(document.querySelector('input[type=text]'), 'input');
// textbox defaults to empty
expect(swatchEl.style.background).toEqual('rgb(255, 255, 255)');
});
});
});