def make_key(original_key): normalised = "".join(character.lower() for character in original_key if character not in " _-',.()+&") if '?' in SanitiseASCII.encode(normalised): return normalised return SanitiseASCII.encode(normalised)
def make_key(original_key): original_key = original_key.replace('&', 'and') original_key = original_key.replace('+', 'and') normalised = "".join(character.lower() for character in original_key if character not in " _-'’,.()") if '?' in SanitiseASCII.encode(normalised): return normalised return SanitiseASCII.encode(normalised)
def useful_headers_after_request(response): response.headers.add("X-Frame-Options", "deny") response.headers.add("X-Content-Type-Options", "nosniff") response.headers.add("X-XSS-Protection", "1; mode=block") response.headers.add("Permissions-Policy", "interest-cohort=()") response.headers.add( "Content-Security-Policy", ( "default-src 'self' {asset_domain} 'unsafe-inline';" "script-src 'self' {asset_domain} *.google-analytics.com *.googletagmanager.com 'unsafe-inline' 'unsafe-eval' data:;" "connect-src 'self' *.google-analytics.com;" "object-src 'self';" "style-src 'self' *.googleapis.com 'unsafe-inline';" "font-src 'self' {asset_domain} *.googleapis.com *.gstatic.com data:;" "img-src 'self' {asset_domain} *.google-analytics.com *.notifications.service.gov.uk data:;" # noqa: E501 "frame-src 'self' www.youtube.com;".format( asset_domain=current_app.config["ASSET_DOMAIN"], )), ) if "Cache-Control" in response.headers: del response.headers["Cache-Control"] # Cache static assets (CSS, JS, images) for a long time # as they have unique hashes thanks to the asset # fingerprinter if asset_fingerprinter.is_static_asset(request.url): response.headers.add("Cache-Control", "public, max-age=31536000, immutable") else: response.headers.add("Cache-Control", "no-store, no-cache, private, must-revalidate") for key, value in response.headers: response.headers[key] = SanitiseASCII.encode(value) return response
def upload_contact_list(service_id): form = CsvUploadForm() if form.validate_on_submit(): try: upload_id = ContactList.upload( current_service.id, Spreadsheet.from_file_form(form).as_dict, ) file_name_metadata = unicode_truncate( SanitiseASCII.encode(form.file.data.filename), 1600) ContactList.set_metadata(current_service.id, upload_id, original_file_name=file_name_metadata) return redirect( url_for( '.check_contact_list', service_id=service_id, upload_id=upload_id, )) except (UnicodeDecodeError, BadZipFile, XLRDError): flash( 'Could not read {}. Try using a different file format.'.format( form.file.data.filename)) except (XLDateError): flash(( '{} contains numbers or dates that Notify cannot understand. ' 'Try formatting all columns as ‘text’ or export your file as CSV.' ).format(form.file.data.filename)) return render_template( 'views/uploads/contact-list/upload.html', form=form, allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS, )
def useful_headers_after_request(response): response.headers.add('X-Frame-Options', 'deny') response.headers.add('X-Content-Type-Options', 'nosniff') response.headers.add('X-XSS-Protection', '1; mode=block') response.headers.add( 'Content-Security-Policy', ( "default-src 'self' {asset_domain} 'unsafe-inline';" "script-src 'self' {asset_domain} *.google-analytics.com *.googletagmanager.com 'unsafe-inline' 'unsafe-eval' data:;" "connect-src 'self' *.google-analytics.com;" "object-src 'self';" "style-src 'self' *.googleapis.com 'unsafe-inline';" "font-src 'self' {asset_domain} *.googleapis.com *.gstatic.com data:;" "img-src 'self' {asset_domain} *.google-analytics.com *.notifications.service.gov.uk {logo_domain} notification-alpha-canada-ca-cdn.s3.amazonaws.com data:;" # noqa: E501 "frame-src 'self' www.youtube.com;".format( asset_domain=current_app.config['ASSET_DOMAIN'], logo_domain=get_logo_cdn_domain(), ))) if 'Cache-Control' in response.headers: del response.headers['Cache-Control'] response.headers.add('Cache-Control', 'no-store, no-cache, private, must-revalidate') for key, value in response.headers: response.headers[key] = SanitiseASCII.encode(value) return response
def check_messages(service_id, template_id, upload_id, row_index=2): data = _check_messages(service_id, template_id, upload_id, row_index) if (data['recipients'].too_many_rows or not data['count_of_recipients'] or not data['recipients'].has_recipient_columns or data['recipients'].duplicate_recipient_column_headers or data['recipients'].missing_column_headers): return render_template('views/check/column-errors.html', **data) if data['row_errors']: return render_template('views/check/row-errors.html', **data) if (data['errors'] or data['trying_to_send_letters_in_trial_mode']): return render_template('views/check/column-errors.html', **data) data['original_file_name'] = SanitiseASCII.encode( data.get('original_file_name', '')) set_metadata_on_csv_upload( service_id, upload_id, notification_count=data['count_of_recipients'], template_id=str(template_id), valid=True, original_file_name=unicode_truncate( data['original_file_name'], 1600, ), ) return render_template('views/check/ok.html', **data)
def useful_headers_after_request(response): response.headers.add('X-Frame-Options', 'deny') response.headers.add('X-Content-Type-Options', 'nosniff') response.headers.add('X-XSS-Protection', '1; mode=block') response.headers.add('Content-Security-Policy', ( "default-src 'self' {asset_domain} 'unsafe-inline';" "script-src 'self' {asset_domain} *.google-analytics.com 'unsafe-inline' 'unsafe-eval' data:;" "connect-src 'self' *.google-analytics.com;" "object-src 'self';" "font-src 'self' {asset_domain} data:;" "img-src 'self' {asset_domain} *.tile.openstreetmap.org *.google-analytics.com" " *.notifications.service.gov.uk {logo_domain} data:;" "frame-src 'self' www.youtube-nocookie.com;".format( asset_domain=current_app.config['ASSET_DOMAIN'], logo_domain=get_logo_cdn_domain(), ))) response.headers.add( 'Link', ('<{asset_url}>; rel=dns-prefetch, <{asset_url}>; rel=preconnect'. format(asset_url=f'https://{current_app.config["ASSET_DOMAIN"]}'))) if 'Cache-Control' in response.headers: del response.headers['Cache-Control'] response.headers.add('Cache-Control', 'no-store, no-cache, private, must-revalidate') for key, value in response.headers: response.headers[key] = SanitiseASCII.encode(value) return response
def format_recipient(address): ''' To format the recipient we need to: - remove new line characters - remove whitespace around the lines - join the address lines, separated by a comma - convert the string to ASCII (S3 metadata must be stored as ASCII) ''' stripped_address_lines_no_trailing_commas = [ line.lstrip().rstrip(' ,') for line in address.splitlines() if line ] one_line_address = ', '.join(stripped_address_lines_no_trailing_commas) return SanitiseASCII.encode(one_line_address)
def useful_headers_after_request(response): response.headers.add('X-Frame-Options', 'deny') response.headers.add('X-Content-Type-Options', 'nosniff') response.headers.add('X-XSS-Protection', '1; mode=block') response.headers.add('Content-Security-Policy', ( "default-src 'self' 'unsafe-inline';" "script-src 'self' *.google-analytics.com 'unsafe-inline' 'unsafe-eval' data:;" "connect-src 'self' *.google-analytics.com;" "object-src 'self';" "font-src 'self' data:;" "img-src 'self' *.google-analytics.com *.notifications.service.gov.uk {} data:;" "frame-src www.youtube.com;".format(get_cdn_domain()))) if 'Cache-Control' in response.headers: del response.headers['Cache-Control'] response.headers.add('Cache-Control', 'no-store, no-cache, private, must-revalidate') for key, value in response.headers: response.headers[key] = SanitiseASCII.encode(value) return response
def check_messages(service_id, template_id, upload_id, row_index=2): data = _check_messages(service_id, template_id, upload_id, row_index) if ( data['recipients'].too_many_rows or not data['count_of_recipients'] or not data['recipients'].has_recipient_columns or data['recipients'].duplicate_recipient_column_headers or data['recipients'].missing_column_headers or data['sent_previously'] ): return render_template('views/check/column-errors.html', **data) if data['row_errors']: return render_template('views/check/row-errors.html', **data) if ( data['errors'] or data['trying_to_send_letters_in_trial_mode'] ): return render_template('views/check/column-errors.html', **data) data['original_file_name'] = SanitiseASCII.encode(data.get('original_file_name', '')) metadata_kwargs = { 'notification_count': data['count_of_recipients'], 'template_id': str(template_id), 'valid': True, 'original_file_name': unicode_truncate( data['original_file_name'], 1600, ), } if session.get('sender_id'): metadata_kwargs['sender_id'] = session['sender_id'] set_metadata_on_csv_upload(service_id, upload_id, **metadata_kwargs) return render_template('views/check/ok.html', **data)
def check_messages(service_id, template_id, upload_id, row_index=2): data = _check_messages(service_id, template_id, upload_id, row_index) if (data["recipients"].too_many_rows or not data["count_of_recipients"] or not data["recipients"].has_recipient_columns or data["recipients"].duplicate_recipient_column_headers or data["recipients"].missing_column_headers or data["sent_previously"]): return render_template("views/check/column-errors.html", **data) if data["row_errors"]: return render_template("views/check/row-errors.html", **data) if data["errors"] or data["trying_to_send_letters_in_trial_mode"]: return render_template("views/check/column-errors.html", **data) data["original_file_name"] = SanitiseASCII.encode( data.get("original_file_name", "")) metadata_kwargs = { "notification_count": data["count_of_recipients"], "template_id": str(template_id), "valid": True, "original_file_name": unicode_truncate( data["original_file_name"], 1600, ), } if session.get("sender_id"): metadata_kwargs["sender_id"] = session["sender_id"] set_metadata_on_csv_upload(service_id, upload_id, **metadata_kwargs) return render_template("views/check/ok.html", **data)
def test_encode_string(content, expected): assert SanitiseSMS.encode(content) == expected assert SanitiseASCII.encode(content) == expected
def test_encode_chars_different_between_ascii_and_sms(char, expected_sms, expected_ascii): assert SanitiseSMS.encode_char(char) == expected_sms assert SanitiseASCII.encode_char(char) == expected_ascii
def send_messages(service_id, template_id): db_template = current_service.get_template_with_user_permission_or_403( template_id, current_user) email_reply_to = None sms_sender = None if db_template['template_type'] == 'email': email_reply_to = get_email_reply_to_address_from_session() elif db_template['template_type'] == 'sms': sms_sender = get_sms_sender_from_session() if db_template[ 'template_type'] not in current_service.available_template_types: return redirect( url_for('.action_blocked', service_id=service_id, notification_type=db_template['template_type'], return_to='view_template', template_id=template_id)) template = get_template( db_template, current_service, show_recipient=True, letter_preview_url=url_for( 'no_cookie.view_letter_template_preview', service_id=service_id, template_id=template_id, filetype='png', page_count=get_page_count_for_letter(db_template), ), email_reply_to=email_reply_to, sms_sender=sms_sender, ) form = CsvUploadForm() if form.validate_on_submit(): try: upload_id = s3upload(service_id, Spreadsheet.from_file_form(form).as_dict, current_app.config['AWS_REGION']) file_name_metadata = unicode_truncate( SanitiseASCII.encode(form.file.data.filename), 1600) set_metadata_on_csv_upload(service_id, upload_id, original_file_name=file_name_metadata) return redirect( url_for( '.check_messages', service_id=service_id, upload_id=upload_id, template_id=template.id, )) except (UnicodeDecodeError, BadZipFile, XLRDError): flash( 'Could not read {}. Try using a different file format.'.format( form.file.data.filename)) except (XLDateError): flash(( '{} contains numbers or dates that Notify cannot understand. ' 'Try formatting all columns as ‘text’ or export your file as CSV.' ).format(form.file.data.filename)) column_headings = get_spreadsheet_column_headings_from_template(template) return render_template( 'views/send.html', template=template, column_headings=list(ascii_uppercase[:len(column_headings)]), example=[column_headings, get_example_csv_rows(template)], form=form, allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS)
def test_encode_chars_different_between_ascii_gsm(char, expected_gsm, expected_ascii): assert SanitiseGSM.encode_char(char) == expected_gsm assert SanitiseASCII.encode_char(char) == expected_ascii
def check_contact_list(service_id, upload_id): form = CsvUploadForm() contents = ContactList.download(service_id, upload_id) first_row = contents.splitlines()[0].strip().rstrip( ',') if contents else '' template_type = { 'emailaddress': 'email', 'phonenumber': 'sms', }.get(Columns.make_key(first_row)) original_file_name = SanitiseASCII.encode( request.args.get('original_file_name', '')) recipients = RecipientCSV( contents, template=get_sample_template(template_type or 'sms'), whitelist=itertools.chain.from_iterable( [user.name, user.mobile_number, user.email_address] for user in current_service.active_users) if current_service.trial_mode else None, allow_international_sms=current_service.has_permission( 'international_sms'), max_initial_rows_shown=50, max_errors_shown=50, ) non_empty_column_headers = list(filter(None, recipients.column_headers)) if len(non_empty_column_headers ) > 1 or not template_type or not recipients: return render_template( 'views/uploads/contact-list/too-many-columns.html', recipients=recipients, original_file_name=original_file_name, template_type=template_type, form=form, ) if recipients.too_many_rows or not len(recipients): return render_template( 'views/uploads/contact-list/column-errors.html', recipients=recipients, original_file_name=original_file_name, form=form, ) row_errors = get_errors_for_csv(recipients, template_type) if row_errors: return render_template( 'views/uploads/contact-list/row-errors.html', recipients=recipients, original_file_name=original_file_name, row_errors=row_errors, form=form, ) if recipients.has_errors: return render_template( 'views/uploads/contact-list/column-errors.html', recipients=recipients, original_file_name=original_file_name, form=form, ) metadata_kwargs = { 'row_count': len(recipients), 'valid': True, 'original_file_name': unicode_truncate( original_file_name, 1600, ), 'template_type': template_type } ContactList.set_metadata(service_id, upload_id, **metadata_kwargs) return render_template( 'views/uploads/contact-list/ok.html', recipients=recipients, original_file_name=original_file_name, upload_id=upload_id, )