diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py
index 1c324736..fab3707c 100644
--- a/atst/forms/task_order.py
+++ b/atst/forms/task_order.py
@@ -7,7 +7,7 @@ from wtforms.fields import (
HiddenField,
)
from wtforms.fields.html5 import DateField
-from wtforms.validators import Required, Length, NumberRange, ValidationError
+from wtforms.validators import Required, Length, NumberRange, ValidationError, Regexp
from flask_wtf import FlaskForm
from numbers import Number
@@ -15,6 +15,7 @@ from .data import JEDI_CLIN_TYPES
from .fields import SelectField
from .forms import BaseForm, remove_empty_string
from atst.utils.localization import translate
+from .validators import REGEX_ALPHA_NUMERIC
from flask import current_app as app
MAX_CLIN_AMOUNT = 1000000000
@@ -116,7 +117,10 @@ class AttachmentForm(BaseForm):
filename = HiddenField(
id="attachment_filename",
validators=[
- Length(max=100, message=translate("forms.attachment.filename.length_error"))
+ Length(
+ max=100, message=translate("forms.attachment.filename.length_error")
+ ),
+ Regexp(regex=REGEX_ALPHA_NUMERIC),
],
)
object_name = HiddenField(
@@ -124,7 +128,8 @@ class AttachmentForm(BaseForm):
validators=[
Length(
max=40, message=translate("forms.attachment.object_name.length_error")
- )
+ ),
+ Regexp(regex=REGEX_ALPHA_NUMERIC),
],
)
accept = ".pdf,application/pdf"
diff --git a/atst/forms/validators.py b/atst/forms/validators.py
index a16bf4f5..ebf29a2a 100644
--- a/atst/forms/validators.py
+++ b/atst/forms/validators.py
@@ -8,6 +8,9 @@ import pendulum
from atst.utils.localization import translate
+REGEX_ALPHA_NUMERIC = "^[A-Za-z0-9\-_ \.]*$"
+
+
def DateRange(lower_bound=None, upper_bound=None, message=None):
def _date_range(form, field):
if field.data is None:
diff --git a/js/components/__tests__/upload_input.test.js b/js/components/__tests__/upload_input.test.js
index 43ae48a8..5d613b14 100644
--- a/js/components/__tests__/upload_input.test.js
+++ b/js/components/__tests__/upload_input.test.js
@@ -70,7 +70,7 @@ describe('UploadInput Test', () => {
})
const component = wrapper.find(uploadinput)
- const event = { target: { value: '', files: [{ name: '' }] } }
+ const event = { target: { value: '', files: [{ name: 'sample.pdf' }] } }
component.setMethods({
getUploader: async () => new MockUploader('token', 'objectName'),
diff --git a/js/components/upload_input.js b/js/components/upload_input.js
index cdd9b15e..d7c90bf5 100644
--- a/js/components/upload_input.js
+++ b/js/components/upload_input.js
@@ -1,5 +1,6 @@
import { buildUploader } from '../lib/upload'
import { emitFieldChange } from '../lib/emitters'
+import inputValidations from '../lib/input_validations'
export default {
name: 'uploadinput',
@@ -28,6 +29,7 @@ export default {
changed: false,
uploadError: false,
sizeError: false,
+ filenameError: false,
downloadLink: '',
}
},
@@ -50,6 +52,10 @@ export default {
this.sizeError = true
return
}
+ if (!this.validateFileName(file.name)) {
+ this.filenameError = true
+ return
+ }
const uploader = await this.getUploader()
const response = await uploader.upload(file)
@@ -71,6 +77,10 @@ export default {
this.uploadError = true
}
},
+ validateFileName: function(name) {
+ const regex = inputValidations.restrictedFileName.match
+ return regex.test(name)
+ },
removeAttachment: function(e) {
e.preventDefault()
this.attachment = null
@@ -118,7 +128,8 @@ export default {
return (
(!this.changed && this.initialErrors) ||
this.uploadError ||
- this.sizeError
+ this.sizeError ||
+ this.filenameError
)
},
valid: function() {
diff --git a/js/lib/input_validations.js b/js/lib/input_validations.js
index af74f211..80c16a7e 100644
--- a/js/lib/input_validations.js
+++ b/js/lib/input_validations.js
@@ -104,4 +104,11 @@ export default {
unmask: ['(', ')', '-', ' '],
validationError: 'Please enter a 10-digit phone number',
},
+ restrictedFileName: {
+ mask: false,
+ match: /^[A-Za-z0-9\-_ \.]+$/,
+ unmask: [],
+ validationError:
+ 'File names can only contain the characters A-Z, 0-9, space, hyphen, underscore, and period.',
+ },
}
diff --git a/templates/components/upload_input.html b/templates/components/upload_input.html
index e9183f9c..c86b6ab6 100644
--- a/templates/components/upload_input.html
+++ b/templates/components/upload_input.html
@@ -49,6 +49,9 @@
{{ "forms.task_order.size_error" | translate }}
+
+ {{ "forms.task_order.filename_error" | translate }}
+
{% for error, error_messages in field.errors.items() %}
{{error_messages[0]}}
{% endfor %}
diff --git a/translations.yaml b/translations.yaml
index 310a88ba..99e2d3b0 100644
--- a/translations.yaml
+++ b/translations.yaml
@@ -292,6 +292,7 @@ forms:
task_order:
upload_error: There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.
size_error: The file you have selected is too large. Please choose a file no larger than 64MB.
+ filename_error: File names can only contain the characters A-Z, 0-9, space, hyphen, underscore, and period.
defense_component_label: Select DoD component(s) funding your Portfolio
file_format_not_allowed: Only PDF or PNG files can be uploaded.
number_description: Task order number (13 digits)