@@ -12825,7 +12825,7 @@ define("document/DocumentCommandHandlers", function (require, exports, module) {
1282512825 if (doc.isUntitled()) {
1282612826 return fullPath.substring(fullPath.lastIndexOf("/") + 1);
1282712827 }
12828- return ProjectManager.makeProjectRelativeIfPossible(fullPath);
12828+ return Phoenix.app.getDisplayPath( ProjectManager.makeProjectRelativeIfPossible(fullPath) );
1282912829
1283012830 }
1283112831
@@ -12841,7 +12841,8 @@ define("document/DocumentCommandHandlers", function (require, exports, module) {
1284112841 if (newDocument) {
1284212842 _currentTitlePath = _shortTitleForDocument(newDocument);
1284312843 } else {
12844- _currentTitlePath = ProjectManager.makeProjectRelativeIfPossible(newFile.fullPath);
12844+ const filePath = ProjectManager.makeProjectRelativeIfPossible(newFile.fullPath);
12845+ _currentTitlePath = Phoenix.app.getDisplayPath(filePath);
1284512846 }
1284612847 } else {
1284712848 _currentTitlePath = null;
@@ -31323,15 +31324,18 @@ table tr{background-color:#fff;border-top:1px solid #c6cbd1}table tr:nth-child(2
3132331324 let _staticServerInstance;
3132431325 let projectServerPort = 0;
3132531326
31326- function getNoPreviewURL(){
31327+ function getNoPreviewURL(
31328+ heading = Strings.DESCRIPTION_LIVEDEV_NO_PREVIEW,
31329+ message = Strings.DESCRIPTION_LIVEDEV_NO_PREVIEW_DETAILS
31330+ ){
3132731331 if(!staticServerURL){
3132831332 return `${window.Phoenix.baseURL}assets/phoenix-splash/no-preview.html?jsonInput=`+
31329- encodeURIComponent(`{"heading":"${Strings.DESCRIPTION_LIVEDEV_NO_PREVIEW }",`
31330- +`"details":"${Strings.DESCRIPTION_LIVEDEV_NO_PREVIEW_DETAILS }"}`);
31333+ encodeURIComponent(`{"heading":"${heading }",`
31334+ +`"details":"${message }"}`);
3133131335 }
3133231336 return `${staticServerURL}phoenix-splash/no-preview.html?jsonInput=`+
31333- encodeURIComponent(`{"heading":"${Strings.DESCRIPTION_LIVEDEV_NO_PREVIEW }",`
31334- +`"details":"${Strings.DESCRIPTION_LIVEDEV_NO_PREVIEW_DETAILS }"}`);
31337+ encodeURIComponent(`{"heading":"${heading }",`
31338+ +`"details":"${message }"}`);
3133531339 }
3133631340
3133731341 async function tabLoaderOnline(data) {
@@ -31664,7 +31668,75 @@ table tr{background-color:#fff;border-top:1px solid #c6cbd1}table tr:nth-child(2
3166431668 return _staticServerInstance._getInstrumentedContent(filePath, url);
3166531669 }
3166631670 return Promise.reject("Cannot get content");
31667- };
31671+ }
31672+
31673+ /*getExternalContent - Special Live Preview for External Project Files
31674+ ------------------------------------------------
31675+ Overview:
31676+ - This feature allows for the preview of files that are not part of the current project.
31677+ It's specifically for files that users open in the editor but which are outside the scope
31678+ of the project being worked on. Useful when users want to quickly view or edit files that are not
31679+ part of the project without integrating them into the project's environment.
31680+
31681+ Domain Separation:
31682+ - The previews for these external files are loaded from a static server URL, not the usual live preview
31683+ server URL.
31684+ - This separation ensures that the active live preview environment does not have access to resources
31685+ from external projects.
31686+
31687+ Security Measures:
31688+ - For security reasons, only the content of the currently viewed file is served in this special
31689+ live preview mode.
31690+ - HTML files are specifically not served in the live preview of external project files.
31691+ This decision is a precaution against disk file traversal attacks. For example, if a malicious HTML
31692+ file from a user's documents folder were allowed in the live preview, it could potentially upload
31693+ sensitive contents from the appdata folder to a remote server. By restricting the serving of HTML
31694+ files and isolating the external file preview environment, the system enhances security while still
31695+ providing the flexibility to view external files to a limited extend.*/
31696+ function getExternalContent(url) {
31697+ return new Promise((resolve, reject)=>{
31698+ const currentDocument = DocumentManager.getCurrentDocument();
31699+ const currentFile = currentDocument? currentDocument.file : ProjectManager.getSelectedItem();
31700+ url = new URL(url);
31701+ const requestedFileName = path.basename(url.pathname);
31702+ if(currentFile && currentFile.fullPath.endsWith(requestedFileName)) {
31703+ // serve preview
31704+ const fullPath = currentFile.fullPath;
31705+ if(utils.isMarkdownFile(fullPath)) {
31706+ resolve(_getMarkdown(fullPath));
31707+ return;
31708+ }
31709+ if(utils.isHTMLFile(fullPath)) {
31710+ const pageText = _getRedirectionPage(getNoPreviewURL(
31711+ Strings.DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED,
31712+ Strings.DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED_DETAILS));
31713+ resolve({
31714+ path,
31715+ textContents: pageText
31716+ });
31717+ return;
31718+ }
31719+ fs.readFile(fullPath, fs.BYTE_ARRAY_ENCODING, function (error, binContent) {
31720+ if(error){
31721+ resolve({
31722+ path: fullPath,
31723+ is404: true
31724+ });
31725+ return;
31726+ }
31727+ resolve({
31728+ path: fullPath,
31729+ buffer: binContent
31730+ });
31731+ });
31732+ return;
31733+ }
31734+ resolve({
31735+ path: url,
31736+ is404: true
31737+ });
31738+ });
31739+ }
3166831740
3166931741 /**
3167031742 * See BaseServer#start. Starts listenting to StaticServerDomain events.
@@ -31749,6 +31821,14 @@ table tr{background-color:#fff;border-top:1px solid #c6cbd1}table tr:nth-child(2
3174931821 + `&isLoggingEnabled=${logger.loggingOptions.logLivePreview}`;
3175031822 }
3175131823
31824+ function _getExternalPreviewURL(fullPath) {
31825+ if(utils.isHTMLFile(fullPath)) {
31826+ return getNoPreviewURL(Strings.DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED,
31827+ Strings.DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED_DETAILS);
31828+ }
31829+ return `${staticServerURL}externalProject/${path.basename(fullPath)}`;
31830+ }
31831+
3175231832 function getTabPopoutURL(url) {
3175331833 let openURL = new URL(url);
3175431834 // we tag all externally opened urls with query string parameter phcodeLivePreview="true" to address
@@ -31781,9 +31861,21 @@ table tr{background-color:#fff;border-top:1px solid #c6cbd1}table tr:nth-child(2
3178131861 URL: getNoPreviewURL(),
3178231862 isNoPreview: true
3178331863 });
31864+ return;
3178431865 }
3178531866 const projectRoot = ProjectManager.getProjectRoot().fullPath;
3178631867 let fullPath = currentFile.fullPath;
31868+ if(!ProjectManager.isWithinProject(fullPath)){
31869+ // external project file. Use secure external preview link.
31870+ resolve({
31871+ URL: _getExternalPreviewURL(fullPath),
31872+ filePath: fullPath,
31873+ fullPath: fullPath,
31874+ isMarkdownFile: utils.isMarkdownFile(fullPath),
31875+ isHTMLFile: utils.isHTMLFile(fullPath)
31876+ });
31877+ return;
31878+ }
3178731879 let httpFilePath = null;
3178831880 if(fullPath.startsWith("http://") || fullPath.startsWith("https://")){
3178931881 httpFilePath = fullPath;
@@ -31833,6 +31925,7 @@ table tr{background-color:#fff;border-top:1px solid #c6cbd1}table tr:nth-child(2
3183331925 // node apis
3183431926 exports.tabLoaderOnline = tabLoaderOnline;
3183531927 exports.getContent = getContent;
31928+ exports.getExternalContent = getExternalContent;
3183631929 exports.onLivePreviewMessage = onLivePreviewMessage;
3183731930 exports.PHCODE_LIVE_PREVIEW_QUERY_PARAM = PHCODE_LIVE_PREVIEW_QUERY_PARAM;
3183831931 exports.EVENT_SERVER_READY = EVENT_SERVER_READY;
@@ -32310,9 +32403,11 @@ define("extensionsIntegrated/Phoenix-live-preview/main", function (require, expo
3231032403 // preview breaks sporadically. to alleviate this, we create a new iframe every time.
3231132404 if(!urlPinned) {
3231232405 currentLivePreviewURL = newSrc;
32313- currentPreviewFile = previewDetails.filePath ;
32406+ currentPreviewFile = previewDetails.fullPath ;
3231432407 }
32315- _setTitle(currentPreviewFile);
32408+ let relativeOrFullPath= ProjectManager.makeProjectRelativeIfPossible(currentPreviewFile);
32409+ relativeOrFullPath = Phoenix.app.getDisplayPath(relativeOrFullPath);
32410+ _setTitle(relativeOrFullPath);
3231632411 if(panel.isVisible()) {
3231732412 let newIframe = $(LIVE_PREVIEW_IFRAME_HTML);
3231832413 newIframe.insertAfter($iframe);
@@ -74748,6 +74843,8 @@ define("nls/root/strings", {
7474874843 "DESCRIPTION_LIVEDEV_ENABLE_REVERSE_INSPECT": "false to disable live preview reverse inspect",
7474974844 "DESCRIPTION_LIVEDEV_NO_PREVIEW": "Nothing to preview!",
7475074845 "DESCRIPTION_LIVEDEV_NO_PREVIEW_DETAILS": "Please select an HTML file to preview",
74846+ "DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED": "Preview Unavailable!",
74847+ "DESCRIPTION_LIVEDEV_PREVIEW_RESTRICTED_DETAILS": "This HTML file is not part of the current project. For security reasons, only project files can be live-previewed. To preview this file, open its containing folder as a separate project.",
7475174848 "DESCRIPTION_LIVEDEV_MAIN_HEADING": "Uh Oh! <br>Your current browser doesn't support live preview.",
7475274849 "DESCRIPTION_LIVEDEV_MAIN_SPAN": "Get the best live preview experience by downloading our native apps for Windows, Mac, and Linux from <a href=\"https://phcode.io\" style=\"color: white\">phcode.io</a>.<br>",
7475374850 "DESCRIPTION_LIVEDEV_SECURITY_POPOUT_MESSAGE": "You are about to open a file for live preview. Please proceed only if you trust the source of this project. Click 'Trust Project' to continue, or close this window if you do not trust the source.",
0 commit comments