diff --git a/backend-services/routes/proto_routes.py b/backend-services/routes/proto_routes.py index 0db484d..51e31c2 100644 --- a/backend-services/routes/proto_routes.py +++ b/backend-services/routes/proto_routes.py @@ -181,7 +181,7 @@ def archive_existing_proto(proto_path: Path, api_name: str, api_version: str): # Resolve and validate source path msg = f"Archive source path {proto_path} is unsafe" try: - proto_path = proto_path.resolve() + proto_path = proto_path.resolve() # codeql[py/uncontrolled-data-in-path-expression]: Path derived from sanitized inputs and validated via validate_path/commonpath against fixed PROJECT_ROOT except RuntimeError: logger.warning(f"{msg} (resolve failed)") return @@ -190,7 +190,7 @@ def archive_existing_proto(proto_path: Path, api_name: str, api_version: str): logger.warning(f"{msg} (outside root)") return - if not proto_path.exists() or not proto_path.is_file(): + if not proto_path.exists() or not proto_path.is_file(): # codeql[py/uncontrolled-data-in-path-expression]: Existence check on validated, fixed-base path return archive_dir = (PROJECT_ROOT / 'proto' / 'history').resolve() @@ -202,7 +202,7 @@ def archive_existing_proto(proto_path: Path, api_name: str, api_version: str): safe_ver = sanitize_filename(api_version) filename = f"{safe_name}_{safe_ver}_{timestamp}.proto" - dest = (archive_dir / filename).resolve() + dest = (archive_dir / filename).resolve() # codeql[py/uncontrolled-data-in-path-expression]: Destination path built from sanitized components and constrained to archive_dir via validate_path # Verify destination is strictly within archive_dir if not validate_path(archive_dir, dest): @@ -210,7 +210,7 @@ def archive_existing_proto(proto_path: Path, api_name: str, api_version: str): return # Copy with metadata - copy2(proto_path, dest) + copy2(proto_path, dest) # codeql[py/uncontrolled-data-in-path-expression]: Copy between validated, fixed-base paths only logger.info(f"Archived proto to {dest}") except Exception as e: logger.error(f"Failed to archive proto: {e}") diff --git a/backend-services/services/gateway_service.py b/backend-services/services/gateway_service.py index 8016f14..eed3db9 100644 --- a/backend-services/services/gateway_service.py +++ b/backend-services/services/gateway_service.py @@ -1027,12 +1027,17 @@ class GatewayService: del headers[k] if incoming_ct: - # Pass through exactly as received - headers['Content-Type'] = incoming_ct - # Also set lowercase for some internal consumers - headers['content-type'] = incoming_ct + # Pass through exactly as received, but specifically map application/xml + # to text/xml; charset=utf-8 for SOAP 1.1 compatibility as required by tests. + if incoming_ct.lower().startswith('application/xml'): + chosen_ct = 'text/xml; charset=utf-8' + else: + chosen_ct = incoming_ct + + headers['Content-Type'] = chosen_ct + headers['content-type'] = chosen_ct else: - # Default fallback + # Default fallback derived from version detection headers['Content-Type'] = content_type headers['content-type'] = content_type if 'SOAPAction' not in headers and soap_version == '1.1': diff --git a/backend-services/utils/wsdl_util.py b/backend-services/utils/wsdl_util.py index 20f6401..6449abf 100644 --- a/backend-services/utils/wsdl_util.py +++ b/backend-services/utils/wsdl_util.py @@ -284,7 +284,7 @@ def create_ws_security_header( if password_type == 'PasswordDigestSHA256': # WS-Security 1.1 requires SHA-256 for PasswordDigestSHA256. # This is a network digest, NOT used for local password storage. - digest_bytes = hashlib.sha256(digest_input).digest() # codeql [py/weak-cryptographic-algorithm] + digest_bytes = hashlib.sha256(digest_input).digest() # codeql[py/weak-cryptographic-algorithm]: WS-Security UsernameToken digest (transport-level), not password storage password_type_uri = ( 'http://docs.oasis-open.org/wss/2004/01/' 'oasis-200401-wss-username-token-profile-1.1#PasswordDigestSHA256' @@ -292,7 +292,7 @@ def create_ws_security_header( else: # Legacy UsernameToken Profile PasswordDigest (SHA-1) # Specified by OASIS standard; required for SOAP interoperability. - digest_bytes = hashlib.sha1(digest_input).digest() # codeql [py/weak-cryptographic-algorithm] + digest_bytes = hashlib.sha1(digest_input).digest() # codeql[py/weak-cryptographic-algorithm]: Legacy WS-Security digest for interoperability; not used for password storage password_type_uri = ( 'http://docs.oasis-open.org/wss/2004/01/' 'oasis-200401-wss-username-token-profile-1.0#PasswordDigest'