From 31513d657eb019e2a164162ffb71eedc0ffe8a7f Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 10 Nov 2022 15:26:27 +1100 Subject: [PATCH] fix(developer): handle utf-8 paths safely --- .../web/Keyman.System.HttpServer.Base.pas | 21 +++++++++++++++++++ ...Keyman.Developer.System.HttpServer.App.pas | 4 ++-- ....Developer.System.HttpServer.AppSource.pas | 8 +++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas b/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas index d632e40baf..305f98d97e 100644 --- a/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas +++ b/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas @@ -23,6 +23,8 @@ type AResponseInfo: TIdHTTPResponseInfo); end; +function CrackUTF8ZeroExtendedString(const p: string): string; + implementation uses @@ -30,6 +32,25 @@ uses System.SysUtils; +function CrackUTF8ZeroExtendedString(const p: string): string; +var + s: RawByteString; + i: Integer; +begin + // Indy's UTF8 handling of URLs is *completely* broken. + // We may need to check this with updated versions of Delphi +{$IFNDEF VER330} + ERROR! Check if this is still needed with Delphi update +{$ENDIF} + + SetLength(s, p.Length); + for i := 1 to p.Length do + begin + s[i] := AnsiChar(Ord(p[i])); + end; + Result := UTF8ToString(s); +end; + { TBaseHttpResponder } function TBaseHttpResponder.IncludesParentFolderReference( diff --git a/developer/src/tike/http/Keyman.Developer.System.HttpServer.App.pas b/developer/src/tike/http/Keyman.Developer.System.HttpServer.App.pas index ddc2920f8e..7c38594665 100644 --- a/developer/src/tike/http/Keyman.Developer.System.HttpServer.App.pas +++ b/developer/src/tike/http/Keyman.Developer.System.HttpServer.App.pas @@ -102,7 +102,7 @@ procedure TAppHttpResponder.RespondProject(doc: string; AContext: TIdContext; Exit; end; - path := UTF8ToString(AnsiString(ARequestInfo.Params.Values['path'])); + path := CrackUTF8ZeroExtendedString(ARequestInfo.Params.Values['path']); if (Path <> '') and (not FileExists(path) or not SameText(ExtractFileExt(path), Ext_ProjectSource)) then begin @@ -132,7 +132,7 @@ procedure TAppHttpResponder.RespondProject(doc: string; AContext: TIdContext; Exit; end; - path := UTF8ToString(AnsiString(ARequestInfo.Params.Values['path'])); + path := CrackUTF8ZeroExtendedString(ARequestInfo.Params.Values['path']); if not FileExists(path) or ( not SameText(ExtractFileExt(path), '.ico') and diff --git a/developer/src/tike/http/Keyman.Developer.System.HttpServer.AppSource.pas b/developer/src/tike/http/Keyman.Developer.System.HttpServer.AppSource.pas index 901b7b64b5..bc43d562da 100644 --- a/developer/src/tike/http/Keyman.Developer.System.HttpServer.AppSource.pas +++ b/developer/src/tike/http/Keyman.Developer.System.HttpServer.AppSource.pas @@ -96,7 +96,7 @@ begin if ARequestInfo.Document = '/app/source/file' then begin // TODO: We should be passing a token to the browser for future POST security - Filename := UTF8ToString(AnsiString(ARequestInfo.Params.Values['Filename'])); + Filename := CrackUTF8ZeroExtendedString(ARequestInfo.Params.Values['Filename']); if ARequestInfo.CommandType = hcGET then begin @@ -112,12 +112,12 @@ begin else if ARequestInfo.Document = '/app/source/toucheditor' then begin // Respond files? - Filename := UTF8ToString(AnsiString(ARequestInfo.Params.Values['Filename'])); + Filename := CrackUTF8ZeroExtendedString(ARequestInfo.Params.Values['Filename']); RespondTouchEditor(Filename, AContext, ARequestInfo, AResponseInfo); end else if ARequestInfo.Document = '/app/source/toucheditor/state' then begin - Filename := UTF8ToString(AnsiString(ARequestInfo.Params.Values['Filename'])); + Filename := CrackUTF8ZeroExtendedString(ARequestInfo.Params.Values['Filename']); RespondTouchEditorState(Filename, AContext, ARequestInfo, AResponseInfo); end else if ARequestInfo.Document.StartsWith('/app/source/toucheditor/lib/') then @@ -147,7 +147,7 @@ begin end else if ARequestInfo.CommandType = hcPOST then begin - FData := UTF8ToString(AnsiString(ARequestInfo.Params.Values['State'])); + FData := CrackUTF8ZeroExtendedString(ARequestInfo.Params.Values['State']); RegisterSource(AFilename + '#state', FData, True); end; end;