Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FWF-4209: [Bugfix] Handle formio keyword in path validation #2534

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions forms-flow-api/src/formsflow_api/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ class BusinessErrorCode(ErrorCodeMixin, Enum):
"Invalid response received from admin service",
HTTPStatus.BAD_REQUEST,
)
INVALID_PATH = (
"The path must not contain: exists, export, role, current, logout, import, form, access, token, recaptcha or end with submission/action.", # pylint: disable=line-too-long
HTTPStatus.BAD_REQUEST,
)

def __new__(cls, message, status_code):
"""Constructor."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,42 @@ def validate_form_title(cls, title, exclude_id=None):
raise BusinessException(BusinessErrorCode.FORM_EXISTS)
return True

@staticmethod
def validate_query_parameters(title, name, path):
"""Check if at least one query parameter is provided."""
if not (title or name or path):
raise BusinessException(BusinessErrorCode.INVALID_FORM_VALIDATION_INPUT)

@staticmethod
def validate_path(path):
"""Validate path with formio resevered keywords."""
current_app.logger.debug(f"Validate path for reseverd keyword:{path}")
# Keywords that are invalid as standalone input
restricted_keywords = {
"exists",
"export",
"role",
"current",
"logout",
"import",
"form",
"access",
"token",
"recaptcha",
}

# Forbidden end keywords
forbidden_end_keywords = {"submission", "action"}

if (
path in restricted_keywords
or path
and any(path.endswith(keyword) for keyword in forbidden_end_keywords)
):
raise BusinessException(BusinessErrorCode.INVALID_PATH)

return True

@staticmethod
@user_context
def validate_form_name_path_title(request, **kwargs):
Expand All @@ -806,20 +842,25 @@ def validate_form_name_path_title(request, **kwargs):
f"Title:{title}, Name:{name}, Path:{path}, form_id:{form_id}, parent_form_id: {parent_form_id}"
)

# Check if at least one query parameter is provided
if not (title or name or path):
raise BusinessException(BusinessErrorCode.INVALID_FORM_VALIDATION_INPUT)
FormProcessMapperService.validate_query_parameters(title, name, path)

if title and len(title) > 200:
raise BusinessException(BusinessErrorCode.INVALID_FORM_TITLE_LENGTH)

FormProcessMapperService.validate_title_name_path(title, path, name)

# In case of new form creation, title alone passed form UI
# Trim space & validate path
if not parent_form_id and title:
path = title.replace(" ", "")

if current_app.config.get("MULTI_TENANCY_ENABLED"):
user: UserContext = kwargs["user"]
tenant_key = user.tenant_key
name = f"{tenant_key}-{name}"
path = f"{tenant_key}-{path}"
# Validate path has reserved keywords
FormProcessMapperService.validate_path(path)
# Validate title exists validation on mapper & path, name in formio.
if title:
FormProcessMapperService.validate_form_title(title, parent_form_id)
Expand Down
2 changes: 2 additions & 0 deletions forms-flow-api/src/formsflow_api/services/import_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ def validate_form(
# In case of new import validate title in mapper table & path,name in formio.
FormProcessMapperService.validate_form_title(title, exclude_id=None)
query_params = f"path={path}&name={name}&select=title,path,name,_id"
# Validate path has reserved keywords
FormProcessMapperService.validate_path(path)
current_app.logger.info(f"Validating form exists...{query_params}")
response = self.get_form_by_query(query_params)
return response
Expand Down
16 changes: 16 additions & 0 deletions forms-flow-api/tests/unit/api/test_form_process_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,14 @@ def test_form_name_invalid_form_title(app, client, session, jwt, mock_redis_clie
response.json["message"]
== "Title: Only contain alphanumeric characters, hyphens(not at the start or end), spaces,and must include at least one letter."
)
# Validate for formio reserved keyword on path while new form creation
response = client.get("/form/validate?title=import", headers=headers)
assert response.status_code == 400
assert response.json is not None
assert (
response.json["message"]
== "The path must not contain: exists, export, role, current, logout, import, form, access, token, recaptcha or end with submission/action."
)


def test_form_name_invalid_form_name(app, client, session, jwt, mock_redis_client):
Expand Down Expand Up @@ -650,6 +658,14 @@ def test_form_name_invalid_form_path(app, client, session, jwt, mock_redis_clien
response.json["message"]
== "Path: Only contain alphanumeric characters, hyphens(not at the start or end), no spaces,and must include at least one letter."
)
# Validate for formio reserved keyword on path
response = client.get("/form/validate?path=import", headers=headers)
assert response.status_code == 400
assert response.json is not None
assert (
response.json["message"]
== "The path must not contain: exists, export, role, current, logout, import, form, access, token, recaptcha or end with submission/action."
)


def test_form_name_invalid_form_name_title_path(
Expand Down
Loading