Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ private IStatus setupTools(IProgressMonitor monitor)
List<String> arguemnts = new ArrayList<>();
Map<String, String> env = new HashMap<>(System.getenv());
addGitToEnvironment(env, eimJson.getGitPath());
arguemnts = ToolsUtility.getExportScriptCommand(idfInstalled.getActivationScript());
var activationScriptPath = idfInstalled.getActivationScript();
arguemnts = ToolsUtility.getExportScriptCommand(activationScriptPath);
Comment on lines +103 to +104
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -name "SetupToolsInIde.java" -type f

Repository: espressif/idf-eclipse-plugin

Length of output: 302


🏁 Script executed:

git ls-files | grep -i "SetupToolsInIde.java"

Repository: espressif/idf-eclipse-plugin

Length of output: 156


🏁 Script executed:

cat -n bundles/com.espressif.idf.core/src/com/espressif/idf/core/tools/SetupToolsInIde.java | head -150

Repository: espressif/idf-eclipse-plugin

Length of output: 6618


🏁 Script executed:

git ls-files | grep -i "IdfInstalled.java"

Repository: espressif/idf-eclipse-plugin

Length of output: 156


🏁 Script executed:

cat bundles/com.espressif.idf.core/src/com/espressif/idf/core/tools/vo/IdfInstalled.java

Repository: espressif/idf-eclipse-plugin

Length of output: 1163


🏁 Script executed:

git ls-files | grep -i "ToolsUtility.java"

Repository: espressif/idf-eclipse-plugin

Length of output: 158


🏁 Script executed:

cat bundles/com.espressif.idf.core/src/com/espressif/idf/core/tools/util/ToolsUtility.java

Repository: espressif/idf-eclipse-plugin

Length of output: 3015


Add null-check for activation script path.

If idfInstalled.getActivationScript() returns null, the value will be added directly to the command list in ToolsUtility.getExportScriptCommand() (lines 71, 76, 80) without validation, and subsequently passed to processBuilderFactory.runInBackground() and the environment variables map at line 137. Validate the path before proceeding.

🤖 Prompt for AI Agents
In
bundles/com.espressif.idf.core/src/com/espressif/idf/core/tools/SetupToolsInIde.java
around lines 103-104, add a null-check for idfInstalled.getActivationScript()
before calling ToolsUtility.getExportScriptCommand(...); if activationScriptPath
is null, log a clear error or warning and abort/return early (or throw a
descriptive exception) so you do NOT call getExportScriptCommand with null and
do NOT pass a null value into processBuilderFactory.runInBackground() or the
environment map at line 137; ensure subsequent code paths that expect arguemnts
handle the early return safely.

final ProcessBuilderFactory processBuilderFactory = new ProcessBuilderFactory();
try
{
Expand Down Expand Up @@ -133,6 +134,7 @@ private IStatus setupTools(IProgressMonitor monitor)
monitor.setTaskName("Processing output from activation script"); //$NON-NLS-1$
log("Processing output from activation script", IStatus.INFO); //$NON-NLS-1$
envVarsFromActivationScriptMap = parseEnvKeys(activationScriptOutput);
envVarsFromActivationScriptMap.put("ESP_IDF_ACTIVATION_SCRIPT", activationScriptPath);
monitor.worked(1);

monitor.setTaskName("Setting up IDE environment"); //$NON-NLS-1$
Expand Down
4 changes: 2 additions & 2 deletions bundles/com.espressif.idf.terminal.connector/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<!-- uses process connector -->
<extension point="org.eclipse.tm.terminal.control.connectors">
<connector
class="org.eclipse.tm.terminal.connector.process.ProcessConnector"
class="com.espressif.idf.terminal.connector.launcher.IdfTerminalProcessConnector"
hidden="true"
id="com.espressif.idf.terminal.connector.espidfConnector"
name="%TerminalConnector.local"/>
Expand All @@ -21,4 +21,4 @@
</delegate>
</extension>

</plugin>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.cdt.utils.pty.PTY;
import org.eclipse.core.runtime.Assert;
Expand Down Expand Up @@ -49,9 +45,6 @@
import org.eclipse.ui.WorkbenchEncoding;
import org.osgi.framework.Bundle;

import com.espressif.idf.core.IDFEnvironmentVariables;
import com.espressif.idf.core.util.IDFUtil;
import com.espressif.idf.core.util.StringUtil;
import com.espressif.idf.terminal.connector.activator.UIPlugin;
import com.espressif.idf.terminal.connector.controls.IDFConsoleWizardConfigurationPanel;

Expand Down Expand Up @@ -196,11 +189,7 @@ public <T> T getAdapter(Class<T> adapter) {
private final File defaultShell() {
String shell = null;
if (Platform.OS_WIN32.equals(Platform.getOS())) {
if (System.getenv("ComSpec") != null && !"".equals(System.getenv("ComSpec").trim())) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
shell = System.getenv("ComSpec").trim(); //$NON-NLS-1$
} else {
shell = "cmd.exe"; //$NON-NLS-1$
}
shell = "powershell.exe"; //$NON-NLS-1$
}
if (shell == null) {
shell = org.eclipse.tm.terminal.view.ui.activator.UIPlugin.getScopedPreferences()
Expand All @@ -221,12 +210,13 @@ private final File defaultShell() {
public ITerminalConnector createTerminalConnector(Map<String, Object> properties) {
Assert.isNotNull(properties);

// Check for the terminal connector id
// 1. Check for the terminal connector id
String connectorId = (String) properties.get(ITerminalsConnectorConstants.PROP_TERMINAL_CONNECTOR_ID);
if (connectorId == null)
if (connectorId == null) {
connectorId = ESP_IDF_CONSOLE_CONNECTOR_ID;
}

// Extract the process properties using defaults
// 2. Extract the process image (shell)
String image;
if (!properties.containsKey(ITerminalsConnectorConstants.PROP_PROCESS_PATH)
|| properties.get(ITerminalsConnectorConstants.PROP_PROCESS_PATH) == null) {
Expand All @@ -236,30 +226,7 @@ public ITerminalConnector createTerminalConnector(Map<String, Object> properties
image = (String) properties.get(ITerminalsConnectorConstants.PROP_PROCESS_PATH);
}

String arguments = (String) properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ARGS);
if (arguments == null && !Platform.OS_WIN32.equals(Platform.getOS())) {
arguments = org.eclipse.tm.terminal.view.ui.activator.UIPlugin.getScopedPreferences()
.getString(IPreferenceKeys.PREF_LOCAL_TERMINAL_DEFAULT_SHELL_UNIX_ARGS);
}

// Avoding profiles to isolate PATH enviroment
arguments = ""; //$NON-NLS-1$
if (image.contains("bash")) { //$NON-NLS-1$
arguments = "--noprofile --norc"; //$NON-NLS-1$
} else if (image.contains("zsh")) { //$NON-NLS-1$
arguments = "--no-rcs --no-globalrcs"; //$NON-NLS-1$
} else if (image.contains("powershell")) { //$NON-NLS-1$
arguments = "-NoProfile"; //$NON-NLS-1$
} else if (Platform.OS_WIN32.equals(Platform.getOS()) && image.contains("cmd.exe")) { //$NON-NLS-1$
// This is the new part that rewrites the arguments for cmd.exe
String title = (String) properties.get(ITerminalsConnectorConstants.PROP_TITLE);
if (title != null && !title.isEmpty()) {
String safeTitle = title.replaceAll("[\\r\\n\"&|<>^]", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$
arguments = "/c \"title " + safeTitle + " && cmd.exe\""; //$NON-NLS-1$ //$NON-NLS-2$
}
}

// Determine if a PTY will be used
// 3. Determine PTY and Echo settings
boolean isUsingPTY = (properties.get(ITerminalsConnectorConstants.PROP_PROCESS_OBJ) == null
&& PTY.isSupported(PTY.Mode.TERMINAL))
|| properties.get(ITerminalsConnectorConstants.PROP_PTY_OBJ) instanceof PTY;
Expand All @@ -275,10 +242,10 @@ public ITerminalConnector createTerminalConnector(Map<String, Object> properties
localEcho = ((Boolean) properties.get(ITerminalsConnectorConstants.PROP_LOCAL_ECHO)).booleanValue();
}

// 4. Determine Line Separator
String lineSeparator = null;
if (!properties.containsKey(ITerminalsConnectorConstants.PROP_LINE_SEPARATOR)
|| !(properties.get(ITerminalsConnectorConstants.PROP_LINE_SEPARATOR) instanceof String)) {
// No line separator will be set if a PTY is used
if (!isUsingPTY) {
lineSeparator = Platform.OS_WIN32.equals(Platform.getOS()) ? ILineSeparatorConstants.LINE_SEPARATOR_CRLF
: ILineSeparatorConstants.LINE_SEPARATOR_LF;
Expand All @@ -287,6 +254,7 @@ public ITerminalConnector createTerminalConnector(Map<String, Object> properties
lineSeparator = (String) properties.get(ITerminalsConnectorConstants.PROP_LINE_SEPARATOR);
}

// 5. Extract other properties
Process process = (Process) properties.get(ITerminalsConnectorConstants.PROP_PROCESS_OBJ);
PTY pty = (PTY) properties.get(ITerminalsConnectorConstants.PROP_PTY_OBJ);
ITerminalServiceOutputStreamMonitorListener[] stdoutListeners = (ITerminalServiceOutputStreamMonitorListener[]) properties
Expand All @@ -295,96 +263,31 @@ public ITerminalConnector createTerminalConnector(Map<String, Object> properties
.get(ITerminalsConnectorConstants.PROP_STDERR_LISTENERS);
String workingDir = (String) properties.get(ITerminalsConnectorConstants.PROP_PROCESS_WORKING_DIR);

String[] envp = null;
if (properties.containsKey(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT)
&& properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT) != null
&& properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT) instanceof String[]) {
envp = (String[]) properties.get(ITerminalsConnectorConstants.PROP_PROCESS_ENVIRONMENT);
}

// Set the ECLIPSE_HOME and ECLIPSE_WORKSPACE environment variables
List<String> envpList = new ArrayList<>();
if (envp != null)
envpList.addAll(Arrays.asList(envp));

// ECLIPSE_HOME
String eclipseHomeLocation = System.getProperty("eclipse.home.location"); //$NON-NLS-1$
if (eclipseHomeLocation != null) {
try {
URI uri = URIUtil.fromString(eclipseHomeLocation);
File f = URIUtil.toFile(uri);
envpList.add("ECLIPSE_HOME=" + f.getAbsolutePath()); //$NON-NLS-1$
} catch (URISyntaxException e) {
/* ignored on purpose */ }
}

// ECLIPSE_WORKSPACE
Bundle bundle = Platform.getBundle("org.eclipse.core.resources"); //$NON-NLS-1$
if (bundle != null && bundle.getState() != Bundle.UNINSTALLED && bundle.getState() != Bundle.STOPPING) {
if (org.eclipse.core.resources.ResourcesPlugin.getWorkspace() != null
&& org.eclipse.core.resources.ResourcesPlugin.getWorkspace().getRoot() != null
&& org.eclipse.core.resources.ResourcesPlugin.getWorkspace().getRoot().getLocation() != null) {
envpList.add("ECLIPSE_WORKSPACE=" + org.eclipse.core.resources.ResourcesPlugin.getWorkspace().getRoot() //$NON-NLS-1$
.getLocation().toOSString());
}
}

//Set CDT build environment variables
Map<String, String> envMap = new IDFEnvironmentVariables().getSystemEnvMap();
Set<String> keySet = envMap.keySet();

//Removing path, since we are using PATH
if (envMap.containsKey("PATH") && envMap.containsKey("Path")) { //$NON-NLS-1$ //$NON-NLS-2$
envMap.remove("Path"); //$NON-NLS-1$
}

for (String envKey : keySet) {
String envValue = envMap.get(envKey);
if (envKey.equals("PATH")) //$NON-NLS-1$
{
String idfExtraPaths = IDFUtil.getIDFExtraPaths();
if (!StringUtil.isEmpty(idfExtraPaths)) {
envValue = idfExtraPaths + ":" + envValue; //$NON-NLS-1$
}
}
envpList.add(envKey + "=" + envValue); //$NON-NLS-1$
}

// Convert back into a string array
envp = envpList.toArray(new String[envpList.size()]);

Assert.isTrue(image != null || process != null);

// Construct the terminal settings store
// 6. Construct and Save Settings
ISettingsStore store = new SettingsStore();

// Construct the process settings
ProcessSettings processSettings = new ProcessSettings();
processSettings.setImage(image);
processSettings.setArguments(arguments);
processSettings.setProcess(process);
processSettings.setPTY(pty);
processSettings.setLocalEcho(localEcho);
processSettings.setLineSeparator(lineSeparator);
processSettings.setStdOutListeners(stdoutListeners);
processSettings.setStdErrListeners(stderrListeners);
processSettings.setWorkingDir(workingDir);
processSettings.setEnvironment(envp);

if (properties.containsKey(ITerminalsConnectorConstants.PROP_PROCESS_MERGE_ENVIRONMENT)) {
Object value = properties.get(ITerminalsConnectorConstants.PROP_PROCESS_MERGE_ENVIRONMENT);
processSettings
.setMergeWithNativeEnvironment(value instanceof Boolean ? ((Boolean) value).booleanValue() : false);
.setMergeWithNativeEnvironment(value instanceof Boolean boolValue && boolValue.booleanValue());
}
// And save the settings to the store
processSettings.save(store);

// Construct the terminal connector instance
// 7. Create Connector
ITerminalConnector connector = TerminalConnectorExtension.makeTerminalConnector(connectorId);
if (connector != null) {
// Apply default settings
connector.setDefaultSettings();
// And load the real settings
connector.load(store);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.espressif.idf.terminal.connector.launcher;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

import org.eclipse.core.runtime.Platform;
import org.eclipse.tm.internal.terminal.provisional.api.ITerminalControl;
import org.eclipse.tm.terminal.connector.process.ProcessConnector;

import com.espressif.idf.core.IDFEnvironmentVariables;

public class IdfTerminalProcessConnector extends ProcessConnector {

private static final String ENV_SCRIPT_KEY = "ESP_IDF_ACTIVATION_SCRIPT"; //$NON-NLS-1$

@Override
public void connect(ITerminalControl control) {
super.connect(control);

var process = getProcess();
if (process == null) {
return;
}

String scriptPath = new IDFEnvironmentVariables().getEnvValue(ENV_SCRIPT_KEY);
if (scriptPath == null || scriptPath.isBlank()) {
writeToTerminal(process.getOutputStream(), "Error: ESP-IDF activation script path is missing.\r\n"); //$NON-NLS-1$
return;
}

try {
OutputStream out = process.getOutputStream();
String command;

if (Platform.OS_WIN32.equals(Platform.getOS())) {
// Windows (PowerShell): Remove-Module: Fixes the color/white-screen issue.
command = "Remove-Module PSReadLine -ErrorAction SilentlyContinue; . \"" + scriptPath + "\"\r\n"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
// Linux / macOS (Bash/Zsh):
command = ". \"" + scriptPath + "\"\r\n"; //$NON-NLS-1$ //$NON-NLS-2$
}
Comment on lines +36 to +42
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential command injection vulnerability - sanitize script path.

The scriptPath is directly interpolated into shell commands without escaping. If the path contains special characters (quotes, backticks, $(...) on Unix, or " on Windows), it could lead to command injection or shell errors.

Consider validating the path or escaping shell metacharacters before constructing the command.

🔎 Example mitigation approach
 if (Platform.OS_WIN32.equals(Platform.getOS())) {
     // Windows (PowerShell): Remove-Module: Fixes the color/white-screen issue.
-    command = "Remove-Module PSReadLine -ErrorAction SilentlyContinue; . \"" + scriptPath + "\"\r\n";
+    // Escape embedded quotes in path for PowerShell
+    String escapedPath = scriptPath.replace("\"", "`\"");
+    command = "Remove-Module PSReadLine -ErrorAction SilentlyContinue; . \"" + escapedPath + "\"\r\n";
 } else {
     // Linux / macOS (Bash/Zsh):
-    command = ". \"" + scriptPath + "\"\r\n";
+    // Escape embedded quotes/special chars for shell
+    String escapedPath = scriptPath.replace("'", "'\\''");
+    command = ". '" + escapedPath + "'\r\n";
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Platform.OS_WIN32.equals(Platform.getOS())) {
// Windows (PowerShell): Remove-Module: Fixes the color/white-screen issue.
command = "Remove-Module PSReadLine -ErrorAction SilentlyContinue; . \"" + scriptPath + "\"\r\n"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
// Linux / macOS (Bash/Zsh):
command = ". \"" + scriptPath + "\"\r\n"; //$NON-NLS-1$ //$NON-NLS-2$
}
if (Platform.OS_WIN32.equals(Platform.getOS())) {
// Windows (PowerShell): Remove-Module: Fixes the color/white-screen issue.
// Escape embedded quotes in path for PowerShell
String escapedPath = scriptPath.replace("\"", "`\"");
command = "Remove-Module PSReadLine -ErrorAction SilentlyContinue; . \"" + escapedPath + "\"\r\n"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
// Linux / macOS (Bash/Zsh):
// Escape embedded quotes/special chars for shell
String escapedPath = scriptPath.replace("'", "'\\''");
command = ". '" + escapedPath + "'\r\n"; //$NON-NLS-1$ //$NON-NLS-2$
}
🤖 Prompt for AI Agents
In
bundles/com.espressif.idf.terminal.connector/src/com/espressif/idf/terminal/connector/launcher/IdfTerminalProcessConnector.java
around lines 36 to 42, the code directly interpolates scriptPath into a shell
command which can lead to command injection or shell parsing errors; instead
validate and sanitize the path and avoid composing a single shell command
string. Fix by verifying the scriptPath is an absolute existing file with no
null bytes and matches an allowed pattern (no unescaped quotes, backticks or
shell metacharacters), then either (preferred) invoke the script via a safe
argument-based API (e.g., use ProcessBuilder with the shell and script passed as
separate arguments or execute the script file directly) or escape/quote the
scriptPath properly for the target shell (escape embedded double quotes on
Windows and properly single-quote or use POSIX shell-escaping on Unix); ensure
any added escaping is centralized in a helper and add unit tests to cover
malicious inputs.


out.write(command.getBytes(StandardCharsets.UTF_8));
out.flush();

} catch (IOException e) {
e.printStackTrace();
}
Comment on lines +47 to +49
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use proper logging instead of e.printStackTrace().

The project uses Logger.log(e) for exception logging (as seen in SetupToolsInIde.java). Replace printStackTrace() with the project's logging infrastructure for consistency and proper log management.

🔎 Suggested fix
+import com.espressif.idf.core.logging.Logger;
 ...
 } catch (IOException e) {
-    e.printStackTrace();
+    Logger.log(e);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (IOException e) {
e.printStackTrace();
}
} catch (IOException e) {
Logger.log(e);
}
🤖 Prompt for AI Agents
In
bundles/com.espressif.idf.terminal.connector/src/com/espressif/idf/terminal/connector/launcher/IdfTerminalProcessConnector.java
around lines 47 to 49, the catch block currently calls e.printStackTrace();
replace this with the project logging call Logger.log(e) (or the appropriate
static Logger class used elsewhere), ensure the Logger is imported or
fully-qualified, and keep the catch behavior otherwise unchanged so the
exception is recorded via the project's logging infrastructure instead of
printing to stderr.

}

private void writeToTerminal(OutputStream out, String message) {
try {
out.write(message.getBytes(StandardCharsets.UTF_8));
out.flush();
} catch (IOException ignored) {
}
}
}