Skip to content
Merged
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
12 changes: 0 additions & 12 deletions docs/API-Reference/command/Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -824,18 +824,6 @@ Sorts working set by file type
## CMD\_WORKING\_SORT\_TOGGLE\_AUTO
Toggles automatic working set sorting

**Kind**: global variable
<a name="CMD_TOGGLE_SHOW_WORKING_SET"></a>

## CMD\_TOGGLE\_SHOW\_WORKING\_SET
Toggles working set visibility

**Kind**: global variable
<a name="CMD_TOGGLE_SHOW_FILE_TABS"></a>

## CMD\_TOGGLE\_SHOW\_FILE\_TABS
Toggles file tabs visibility

**Kind**: global variable
<a name="CMD_TOGGLE_SHOW_WORKING_SET"></a>

Expand Down
70 changes: 68 additions & 2 deletions src-node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const PHOENIX_FS_URL = `/PhoenixFS${randomNonce(8)}`;
const PHOENIX_STATIC_SERVER_URL = `/Static${randomNonce(8)}`;
const PHOENIX_NODE_URL = `/PhoenixNode${randomNonce(8)}`;
const PHOENIX_LIVE_PREVIEW_COMM_URL = `/PreviewComm${randomNonce(8)}`;
const PHOENIX_AUTO_AUTH_URL = `/AutoAuth${randomNonce(8)}`;

const savedConsoleLog = console.log;

Expand Down Expand Up @@ -190,7 +191,8 @@ function processCommand(line) {
phoenixFSURL: `ws://localhost:${port}${PHOENIX_FS_URL}`,
phoenixNodeURL: `ws://localhost:${port}${PHOENIX_NODE_URL}`,
staticServerURL: `http://localhost:${port}${PHOENIX_STATIC_SERVER_URL}`,
livePreviewCommURL: `ws://localhost:${port}${PHOENIX_LIVE_PREVIEW_COMM_URL}`
livePreviewCommURL: `ws://localhost:${port}${PHOENIX_LIVE_PREVIEW_COMM_URL}`,
autoAuthURL: `http://localhost:${port}${PHOENIX_AUTO_AUTH_URL}`
}, jsonCmd.commandID);
});
return;
Expand All @@ -207,6 +209,67 @@ rl.on('line', (line) => {

const localhostOnly = 'localhost';

const AUTH_CONNECTOR_ID = "ph_auth";
const EVENT_CONNECTED = "connected";
let verificationCode = null;
async function setVerificationCode(code) {
verificationCode = code;
}
const nodeConnector = NodeConnector.createNodeConnector(AUTH_CONNECTOR_ID, {
setVerificationCode
});

const ALLOWED_ORIGIN = 'https://account.phcode.io';
function autoAuth(req, res) {
const origin = req.headers.origin;
// localhost dev of loginService is not allowed here to not leak to production. So autoAuth is not available in
// dev builds.
const isAllowedOrigin = !origin || (ALLOWED_ORIGIN === origin);
if(!isAllowedOrigin){
res.writeHead(403, { 'Content-Type': 'text/plain' });
res.end('Forbidden origin');
return;
}
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Origin', origin);

Check failure

Code scanning / CodeQL

CORS misconfiguration for credentials transfer

[Credential](1) leak vulnerability due to a [misconfigured CORS header value](2). [Credential](3) leak vulnerability due to a [misconfigured CORS header value](2). [Credential](4) leak vulnerability due to a [misconfigured CORS header value](2).

Copilot Autofix

AI about 1 year ago

To fix the issue, the Access-Control-Allow-Origin header must only be set to trusted origins. This can be achieved by validating the origin header against a whitelist of allowed origins before setting it. If the origin is not in the whitelist, the header should not be set, and the request should be rejected.

Steps to fix:

  1. Introduce a whitelist of allowed origins.
  2. Validate the origin header against the whitelist before setting the Access-Control-Allow-Origin header.
  3. Ensure that the validation logic applies to all request types, including OPTIONS preflight requests.

Required changes:

  • Add a whitelist of allowed origins.
  • Update the autoAuth function to validate the origin header against the whitelist before setting the Access-Control-Allow-Origin header.

Suggested changeset 1
src-node/index.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src-node/index.js b/src-node/index.js
--- a/src-node/index.js
+++ b/src-node/index.js
@@ -221,3 +221,3 @@
 
-const ALLOWED_ORIGIN = 'https://account.phcode.io';
+const ALLOWED_ORIGINS = ['https://account.phcode.io'];
 function autoAuth(req, res) {
@@ -226,4 +226,4 @@
     // dev builds.
-    const isAllowedOrigin = !origin || (ALLOWED_ORIGIN === origin);
-    if(!isAllowedOrigin){
+    const isAllowedOrigin = origin && ALLOWED_ORIGINS.includes(origin);
+    if (!isAllowedOrigin) {
         res.writeHead(403, { 'Content-Type': 'text/plain' });
EOF
@@ -221,3 +221,3 @@

const ALLOWED_ORIGIN = 'https://account.phcode.io';
const ALLOWED_ORIGINS = ['https://account.phcode.io'];
function autoAuth(req, res) {
@@ -226,4 +226,4 @@
// dev builds.
const isAllowedOrigin = !origin || (ALLOWED_ORIGIN === origin);
if(!isAllowedOrigin){
const isAllowedOrigin = origin && ALLOWED_ORIGINS.includes(origin);
if (!isAllowedOrigin) {
res.writeHead(403, { 'Content-Type': 'text/plain' });
Copilot is powered by AI and may make mistakes. Always verify output.
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Private-Network', 'true');
res.writeHead(204);
res.end();
return;
}
// Remove '/AutoAuth<rand>' from the beginning of the URL and construct file path
const url = new URL(req.url, `http://${req.headers.host}`);
const cleanPath = url.pathname.replace(PHOENIX_AUTO_AUTH_URL, '');
// Check if the request is for the autoVerifyCode endpoint
if (cleanPath === `/autoVerifyCode` && req.method === 'GET') {
origin && res.setHeader('Access-Control-Allow-Origin', origin);

Check failure

Code scanning / CodeQL

CORS misconfiguration for credentials transfer

[Credential](1) leak vulnerability due to a [misconfigured CORS header value](2). [Credential](3) leak vulnerability due to a [misconfigured CORS header value](2). [Credential](4) leak vulnerability due to a [misconfigured CORS header value](2).

Copilot Autofix

AI about 1 year ago

To fix the issue, we will implement a whitelist of allowed origins and validate the req.headers.origin against this whitelist before setting the Access-Control-Allow-Origin header. This ensures that only trusted origins are allowed, and prevents attackers from exploiting the dynamic computation of the header.

Steps to fix:

  1. Define a whitelist of allowed origins as a constant.
  2. Validate the req.headers.origin against the whitelist.
  3. Set the Access-Control-Allow-Origin header only if the origin is in the whitelist.
  4. Remove redundant checks and ensure consistent validation logic.

Suggested changeset 1
src-node/index.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src-node/index.js b/src-node/index.js
--- a/src-node/index.js
+++ b/src-node/index.js
@@ -221,3 +221,3 @@
 
-const ALLOWED_ORIGIN = 'https://account.phcode.io';
+const ALLOWED_ORIGINS = ['https://account.phcode.io'];
 function autoAuth(req, res) {
@@ -226,4 +226,4 @@
     // dev builds.
-    const isAllowedOrigin = !origin || (ALLOWED_ORIGIN === origin);
-    if(!isAllowedOrigin){
+    const isAllowedOrigin = ALLOWED_ORIGINS.includes(origin);
+    if (!isAllowedOrigin) {
         res.writeHead(403, { 'Content-Type': 'text/plain' });
@@ -247,4 +247,4 @@
     if (cleanPath === `/autoVerifyCode` && req.method === 'GET') {
-        origin && res.setHeader('Access-Control-Allow-Origin', origin);
-        if(!verificationCode) {
+        res.setHeader('Access-Control-Allow-Origin', origin);
+        if (!verificationCode) {
             res.setHeader('Content-Type', 'text/plain');
@@ -261,3 +261,3 @@
         nodeConnector.triggerPeer(EVENT_CONNECTED, "ok");
-        origin && res.setHeader('Access-Control-Allow-Origin', origin);
+        res.setHeader('Access-Control-Allow-Origin', origin);
         res.setHeader('Access-Control-Allow-Credentials', 'true');
EOF
@@ -221,3 +221,3 @@

const ALLOWED_ORIGIN = 'https://account.phcode.io';
const ALLOWED_ORIGINS = ['https://account.phcode.io'];
function autoAuth(req, res) {
@@ -226,4 +226,4 @@
// dev builds.
const isAllowedOrigin = !origin || (ALLOWED_ORIGIN === origin);
if(!isAllowedOrigin){
const isAllowedOrigin = ALLOWED_ORIGINS.includes(origin);
if (!isAllowedOrigin) {
res.writeHead(403, { 'Content-Type': 'text/plain' });
@@ -247,4 +247,4 @@
if (cleanPath === `/autoVerifyCode` && req.method === 'GET') {
origin && res.setHeader('Access-Control-Allow-Origin', origin);
if(!verificationCode) {
res.setHeader('Access-Control-Allow-Origin', origin);
if (!verificationCode) {
res.setHeader('Content-Type', 'text/plain');
@@ -261,3 +261,3 @@
nodeConnector.triggerPeer(EVENT_CONNECTED, "ok");
origin && res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
Copilot is powered by AI and may make mistakes. Always verify output.
if(!verificationCode) {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(404);
res.end('Not Found');
return;
}
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Private-Network', 'true');
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ code: verificationCode }));
verificationCode = null; // verification code is only returned once
} else if (cleanPath === `/appVerified` && req.method === 'GET') {
nodeConnector.triggerPeer(EVENT_CONNECTED, "ok");
origin && res.setHeader('Access-Control-Allow-Origin', origin);

Check failure

Code scanning / CodeQL

CORS misconfiguration for credentials transfer

[Credential](1) leak vulnerability due to a [misconfigured CORS header value](2). [Credential](3) leak vulnerability due to a [misconfigured CORS header value](2). [Credential](4) leak vulnerability due to a [misconfigured CORS header value](2).

Copilot Autofix

AI about 1 year ago

To fix the issue, we need to ensure that the Access-Control-Allow-Origin header is set to a safe and validated value. This can be achieved by using a whitelist of allowed origins and ensuring that the origin header is explicitly validated against this whitelist before being used. Additionally, we should handle edge cases like the null origin to prevent misuse.

Steps to fix:

  1. Introduce a whitelist of allowed origins, including ALLOWED_ORIGIN.
  2. Validate the origin header against the whitelist before setting the Access-Control-Allow-Origin header.
  3. Reject requests with invalid or unsafe origins, including the null origin.
  4. Update the logic in the autoAuth function to use the validated origin.
Suggested changeset 1
src-node/index.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src-node/index.js b/src-node/index.js
--- a/src-node/index.js
+++ b/src-node/index.js
@@ -221,3 +221,3 @@
 
-const ALLOWED_ORIGIN = 'https://account.phcode.io';
+const ALLOWED_ORIGINS = ['https://account.phcode.io'];
 function autoAuth(req, res) {
@@ -226,4 +226,4 @@
     // dev builds.
-    const isAllowedOrigin = !origin || (ALLOWED_ORIGIN === origin);
-    if(!isAllowedOrigin){
+    const isAllowedOrigin = origin && ALLOWED_ORIGINS.includes(origin);
+    if (!isAllowedOrigin || origin === 'null') {
         res.writeHead(403, { 'Content-Type': 'text/plain' });
@@ -247,3 +247,3 @@
     if (cleanPath === `/autoVerifyCode` && req.method === 'GET') {
-        origin && res.setHeader('Access-Control-Allow-Origin', origin);
+        res.setHeader('Access-Control-Allow-Origin', origin);
         if(!verificationCode) {
@@ -261,3 +261,3 @@
         nodeConnector.triggerPeer(EVENT_CONNECTED, "ok");
-        origin && res.setHeader('Access-Control-Allow-Origin', origin);
+        res.setHeader('Access-Control-Allow-Origin', origin);
         res.setHeader('Access-Control-Allow-Credentials', 'true');
EOF
@@ -221,3 +221,3 @@

const ALLOWED_ORIGIN = 'https://account.phcode.io';
const ALLOWED_ORIGINS = ['https://account.phcode.io'];
function autoAuth(req, res) {
@@ -226,4 +226,4 @@
// dev builds.
const isAllowedOrigin = !origin || (ALLOWED_ORIGIN === origin);
if(!isAllowedOrigin){
const isAllowedOrigin = origin && ALLOWED_ORIGINS.includes(origin);
if (!isAllowedOrigin || origin === 'null') {
res.writeHead(403, { 'Content-Type': 'text/plain' });
@@ -247,3 +247,3 @@
if (cleanPath === `/autoVerifyCode` && req.method === 'GET') {
origin && res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Origin', origin);
if(!verificationCode) {
@@ -261,3 +261,3 @@
nodeConnector.triggerPeer(EVENT_CONNECTED, "ok");
origin && res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
Copilot is powered by AI and may make mistakes. Always verify output.
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Private-Network', 'true');
res.setHeader('Content-Type', 'application/json');
res.end("ok");
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
}

// Create an HTTP server
const server = http.createServer((req, res) => {
if (req.url.startsWith(PHOENIX_STATIC_SERVER_URL)) {
Expand Down Expand Up @@ -249,7 +312,9 @@ const server = http.createServer((req, res) => {
}
});

} else {
} else if (req.url.startsWith(PHOENIX_AUTO_AUTH_URL)) {
return autoAuth(req, res);
}else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
}
Expand All @@ -269,5 +334,6 @@ server.listen(0, localhostOnly, () => {
savedConsoleLog(`Phoenix node tauri FS url is ws://localhost:${port}${PHOENIX_FS_URL}`);
savedConsoleLog(`Phoenix node connector url is ws://localhost:${port}${PHOENIX_NODE_URL}`);
savedConsoleLog(`Phoenix live preview comm url is ws://localhost:${port}${PHOENIX_LIVE_PREVIEW_COMM_URL}`);
savedConsoleLog(`Phoenix AutoAuth url is ws://localhost:${port}${PHOENIX_AUTO_AUTH_URL}`);
serverPortResolve(port);
});
1 change: 1 addition & 0 deletions src/brackets.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ define(function (require, exports, module) {
require("widgets/InlineMenu");
require("thirdparty/tinycolor");
require("utils/LocalizationUtils");
require("services/login");

// DEPRECATED: In future we want to remove the global CodeMirror, but for now we
// expose our required CodeMirror globally so as to avoid breaking extensions in the
Expand Down
5 changes: 3 additions & 2 deletions src/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
"app_title": "Phoenix Code",
"app_name_about": "Phoenix Code",
"about_icon": "styles/images/phoenix-icon.svg",
"account_url": "https://account.phcode.io/",
"how_to_use_url": "https://github.com/adobe/brackets/wiki/How-to-Use-Brackets",
"docs_url": "https://docs.phcode.dev/",
"support_url": "https://github.com/phcode-dev/phoenix/discussions",
"support_url": "https://account.phcode.io/?returnUrl=https%3A%2F%2Faccount.phcode.io%2F%23support",
"suggest_feature_url": "https://github.com/phcode-dev/phoenix/discussions/categories/ideas",
"report_issue_url": "https://github.com/phcode-dev/phoenix/issues/new/choose",
"get_involved_url": "https://github.com/phcode-dev/phoenix/discussions/77",
Expand Down Expand Up @@ -59,4 +60,4 @@
"url": "https://github.com/phcode-dev/phoenix/blob/master/LICENSE"
}
]
}
}
20 changes: 1 addition & 19 deletions src/extensionsIntegrated/Phoenix/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*
*/

/*global Phoenix*/
/*eslint no-console: 0*/
/*eslint strict: ["error", "global"]*/
/* jshint ignore:start */
Expand All @@ -32,26 +31,10 @@ define(function (require, exports, module) {
Strings = require("strings"),
Dialogs = require("widgets/Dialogs"),
NotificationUI = require("widgets/NotificationUI"),
DefaultDialogs = require("widgets/DefaultDialogs"),
ProfileMenu = require("./profile-menu");
DefaultDialogs = require("widgets/DefaultDialogs");

const PERSIST_STORAGE_DIALOG_DELAY_SECS = 60000;
let $icon;

function _addToolbarIcon() {
const helpButtonID = "user-profile-button";
$icon = $("<a>")
.attr({
id: helpButtonID,
href: "#",
class: "user",
title: Strings.CMD_USER_PROFILE
})
.appendTo($("#main-toolbar .bottom-buttons"));
$icon.on('click', ()=>{
ProfileMenu.init();
});
}
function _showUnSupportedBrowserDialogue() {
if(Phoenix.browser.isMobile || Phoenix.browser.isTablet){
Dialogs.showModalDialog(
Expand Down Expand Up @@ -109,7 +92,6 @@ define(function (require, exports, module) {
if(Phoenix.isSpecRunnerWindow){
return;
}
_addToolbarIcon();
serverSync.init();
defaultProjects.init();
newProject.init();
Expand Down
26 changes: 24 additions & 2 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ define({
"CMD_AUTO_UPDATE": "Auto Update",
"CMD_HOW_TO_USE_BRACKETS": "How to Use {APP_NAME}",
"CMD_SUPPORT": "{APP_NAME} Support",
"CMD_USER_PROFILE": "User Profile",
"CMD_USER_PROFILE": "{APP_NAME} Account",
"CMD_DOCS": "Help, Getting Started",
"CMD_SUGGEST": "Suggest a Feature",
"CMD_REPORT_ISSUE": "Report Issue",
Expand Down Expand Up @@ -1564,5 +1564,27 @@ define({
"GIT_TOAST_MESSAGE": "Click the Git panel icon to manage your repository. Easily commit, push, pull, and view your project history—all in one place.<br><a href='https://docs.phcode.dev/docs/Features/git'>Learn more about the Git panel →</a>",

// surveys
"SURVEY_TITLE_VOTE_FOR_FEATURES_YOU_WANT": "Vote for the features you want to see next!"
"SURVEY_TITLE_VOTE_FOR_FEATURES_YOU_WANT": "Vote for the features you want to see next!",

// login
"SIGNED_OUT": "You have been signed out.",
"SIGNED_OUT_MESSAGE": "You have been signed out of your {APP_NAME} account. Please sign in again to continue.",
"SIGNED_OUT_MESSAGE_FRIENDLY": "Thank you for using {APP_NAME}. See you soon!",
"SIGNED_IN_OFFLINE_TITLE": "Offline - Cannot Sign In",
"SIGNED_IN_OFFLINE_MESSAGE": "Please connect to the internet to sign in to {APP_NAME}.",
"SIGNED_IN_FAILED_TITLE": "Cannot Sign In",
"SIGNED_IN_FAILED_MESSAGE": "Something went wrong while trying to sign in. Please try again.",
"SIGNED_OUT_FAILED_TITLE": "Failed to Sign Out",
"SIGNED_OUT_FAILED_MESSAGE": "Something went wrong while trying to sign out. Please try again.",
"VALIDATION_CODE_TITLE": "Sign In Verification Code",
"VALIDATION_CODE_MESSAGE": "Please use this Verification code to sign in to your {APP_NAME} account:",
"COPY_VALIDATION_CODE": "Copy Code",
"VALIDATION_CODE_COPIED": "Copied",
"OPEN_SIGN_IN_URL": "Open Sign In Page",
"PROFILE_POP_TITLE": "{APP_NAME} Account",
"PROFILE_SIGN_IN": "Sign in to your account",
"CONTACT_SUPPORT": "Contact support",
"SIGN_OUT": "Sign out",
"ACCOUNT_DETAILS": "Account Details",
"AI_QUOTA_USED": "AI quota used"
});
7 changes: 6 additions & 1 deletion src/node-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@
*
*/

/*global Phoenix, fs, logger*/
/*global fs, logger*/

function nodeLoader() {
const KernalModeTrust = window.KernalModeTrust;
if(!KernalModeTrust){
throw new Error("KernalModeTrust is not defined. Cannot boot nodeLoader without trust ring");
}
const nodeLoadstartTime = Date.now();
const phcodeExecHandlerMap = {};
const nodeConnectorIDMap = {};
Expand Down Expand Up @@ -721,6 +725,7 @@ function nodeLoader() {
fs.setNodeWSEndpoint(message.phoenixFSURL);
fs.forceUseNodeWSEndpoint(true);
setNodeWSEndpoint(message.phoenixNodeURL);
KernalModeTrust.localAutoAuthURL = message.autoAuthURL;
window.isNodeReady = true;
resolve(message);
// node is designed such that it is not required at boot time to lower startup time.
Expand Down
Loading