From 454264db7241175d1b3c2d116b68951de06279c5 Mon Sep 17 00:00:00 2001
From: g1rly-c0d3r
Date: Fri, 2 Oct 2026 12:34:52 -0500
Subject: [PATCH 01/10] fix: group schema import exceptions & add logging
---
specifyweb/backend/context/views.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/specifyweb/backend/context/views.py b/specifyweb/backend/context/views.py
index ed50095bf07..8546623a584 100644
--- a/specifyweb/backend/context/views.py
+++ b/specifyweb/backend/context/views.py
@@ -5,6 +5,7 @@
import json
import os
import re
+import logging
from typing import List
from xml.etree import ElementTree
@@ -14,7 +15,7 @@
from django.db import connection, transaction
from django.db.models import Q
from django.http import Http404, HttpResponse, HttpResponseBadRequest, \
- HttpResponseForbidden, JsonResponse
+ HttpResponseForbidden, HttpResponseServerError, JsonResponse
from django.urls import URLPattern
from django.utils.translation import get_language_info
from django.utils.translation import gettext as _
@@ -46,6 +47,8 @@
is_collection_available,
is_guided_setup_complete,
)
+
+logger = logging.getLogger(__name__)
def set_collection_cookie(response, collection_id): # pragma: no cover
response.set_cookie('collection', str(collection_id), max_age=365*24*60*60)
@@ -655,13 +658,13 @@ def schema_localization_import(request):
if not isinstance(language, str) or not re.fullmatch(
r'[A-Za-z]{2}(?:-[A-Za-z]{2})?', language
):
- raise ValueError
+ raise ValueError(f"Invalid language string: {language}")
if isinstance(schema, dict) and {'language', 'schema'} <= schema.keys():
source_language = schema['language']
if not isinstance(source_language, str) or (
source_language.lower() != language.lower()
):
- raise ValueError
+ raise ValueError(f"Invalid Localization (expected {source_language}, got {language}")
schema = schema['schema']
references = {
'format': _schema_import_resource_names(
@@ -704,8 +707,12 @@ def schema_localization_import(request):
request.specify_collection, request.specify_user_agent,
model.__name__, data
)
- except (AttributeError, KeyError, TypeError, ValueError, json.JSONDecodeError):
- return HttpResponseBadRequest()
+ except (ValueError, json.JSONDecodeError) as err:
+ return HttpResponseBadRequest(f"{{\"original error\": \"{err}\" }}", content_type="text/json")
+ except (AttributeError, KeyError, TypeError) as err:
+ logger.warning(f"Schema Import failed: {err}")
+ return HttpResponseServerError(err, content_type="text/plain")
+
return JsonResponse({'updated': len(operations)})
view_parameters_schema = [
From a23a3189d173238e8f7f06a340a82d95b5c4d6e7 Mon Sep 17 00:00:00 2001
From: "Caroline D." <108160931+CarolineDenis@users.noreply.github.com>
Date: Tue, 6 Oct 2026 14:12:16 +0200
Subject: [PATCH 02/10] Potential fix for pull request finding 'CodeQL /
Information exposure through an exception'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
---
specifyweb/backend/context/views.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/specifyweb/backend/context/views.py b/specifyweb/backend/context/views.py
index 8546623a584..9c6cd620130 100644
--- a/specifyweb/backend/context/views.py
+++ b/specifyweb/backend/context/views.py
@@ -708,7 +708,11 @@ def schema_localization_import(request):
model.__name__, data
)
except (ValueError, json.JSONDecodeError) as err:
- return HttpResponseBadRequest(f"{{\"original error\": \"{err}\" }}", content_type="text/json")
+ logger.warning("Schema import validation failed", exc_info=True)
+ return JsonResponse(
+ {"error": "Invalid schema localization import payload."},
+ status=400
+ )
except (AttributeError, KeyError, TypeError) as err:
logger.warning(f"Schema Import failed: {err}")
return HttpResponseServerError(err, content_type="text/plain")
From 4ce18f89d4659e9f1cf5e8a56de332ae576006ae Mon Sep 17 00:00:00 2001
From: "Caroline D." <108160931+CarolineDenis@users.noreply.github.com>
Date: Tue, 6 Oct 2026 14:12:27 +0200
Subject: [PATCH 03/10] Potential fix for pull request finding 'CodeQL /
Information exposure through an exception'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
---
specifyweb/backend/context/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/specifyweb/backend/context/views.py b/specifyweb/backend/context/views.py
index 9c6cd620130..e3185449c37 100644
--- a/specifyweb/backend/context/views.py
+++ b/specifyweb/backend/context/views.py
@@ -715,7 +715,7 @@ def schema_localization_import(request):
)
except (AttributeError, KeyError, TypeError) as err:
logger.warning(f"Schema Import failed: {err}")
- return HttpResponseServerError(err, content_type="text/plain")
+ return HttpResponseServerError("An internal error has occurred.", content_type="text/plain")
return JsonResponse({'updated': len(operations)})
From d0dadbce042bbec9a11805fd7a37bf38907d48d9 Mon Sep 17 00:00:00 2001
From: g1rly-c0d3r
Date: Tue, 6 Oct 2026 12:32:25 -0500
Subject: [PATCH 04/10] fix: add reporting on the front-end to distinguish
error types
---
.../lib/components/SchemaConfig/Layout.tsx | 38 ++++++-------------
.../js_src/lib/localization/schema.ts | 26 ++++++++++++-
2 files changed, 37 insertions(+), 27 deletions(-)
diff --git a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx
index 161dfc818d4..8f41091e6c3 100644
--- a/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx
+++ b/specifyweb/frontend/js_src/lib/components/SchemaConfig/Layout.tsx
@@ -24,6 +24,7 @@ import {
useSchemaConfig,
} from './Store';
import { dialogIcons } from '../Atoms/Icons';
+import { raise } from '../Errors/Crash';
export function SchemaConfigLayout(): JSX.Element {
const schemaData = useOutletContext();
@@ -55,8 +56,7 @@ function SchemaConfigLayoutContent(): JSX.Element {
const setSingleResource = React.useContext(SetSingleResourceContext);
const loading = React.useContext(LoadingContext);
const [importFile, setImportFile] = React.useState();
- const [importError, setImportError] = React.useState(false);
- const [importSuccessful, setImportSuccessful] = React.useState(false);
+ const [importStatus, setImportStatus] = React.useState(0);
const [importRefreshError, setImportRefreshError] = React.useState(false);
React.useEffect(() => {
@@ -80,7 +80,6 @@ function SchemaConfigLayoutContent(): JSX.Element {
);
};
const handleImport = (file: File): void => {
- setImportError(false);
setImportFile(file);
};
const confirmImport = (): void => {
@@ -95,17 +94,22 @@ function SchemaConfigLayoutContent(): JSX.Element {
method: 'POST',
headers: { Accept: 'application/json' },
body: { schema, language: rawLanguage },
- errorMode: 'silent',
+ expectedErrors: [400, 504]
})
)
.then(
- () => setImportSuccessful(true),
- () => setImportError(true)
+ (response) => {
+ setImportStatus(response.status);
+ if (response.status != 200)
+ raise({name: schemaText.importSchemaError({schemaConfig: schemaText.schemaConfig()}), message:
+ response.status === 504 ? schemaText.importSchemaErrorTimeout({schemaConfig: schemaText.schemaConfig()}) : schemaText.importSchemaErrorBadRequest({schemaConfig: schemaText.schemaConfig()})
+ })
+ }
)
);
};
const closeImportSuccess = (): void => {
- setImportSuccessful(false);
+ setImportStatus(0);
loading(
handleSchemaSaved(rawLanguage, tableName).catch(() =>
setImportRefreshError(true)
@@ -170,25 +174,7 @@ function SchemaConfigLayoutContent(): JSX.Element {
)}
- {importError && (
-
- )}
- {importSuccessful && (
+ {importStatus == 200 && (