diff --git a/build.gradle b/build.gradle index 147565f2..d660208d 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ repositories { } } -def buildInfoVersion = '2.43.6' +def buildInfoVersion = '2.43.x-SNAPSHOT' // Updated to 2.17.3 for security fixes - compatible with Java 8+ def jacksonVersion = '2.17.3' diff --git a/src/main/java/com/jfrog/ide/common/go/GoComponentUpdater.java b/src/main/java/com/jfrog/ide/common/go/GoComponentUpdater.java index 08edfa5a..8663e02d 100644 --- a/src/main/java/com/jfrog/ide/common/go/GoComponentUpdater.java +++ b/src/main/java/com/jfrog/ide/common/go/GoComponentUpdater.java @@ -3,6 +3,7 @@ import com.jfrog.ide.common.updateversion.ComponentUpdater; import org.jfrog.build.api.util.Log; import org.jfrog.build.extractor.go.GoDriver; +import org.jfrog.build.extractor.WslUtils; import java.io.IOException; import java.nio.file.Path; @@ -19,7 +20,7 @@ public class GoComponentUpdater extends ComponentUpdater { public GoComponentUpdater(Path projectDir, Log logger, Map env, String executablePath) { super(projectDir, logger); - this.goDriver = new GoDriver(executablePath, env, projectDir.toFile(), logger); + this.goDriver = new GoDriver(executablePath, env, projectDir.toFile(), logger, WslUtils.isWslPath(projectDir)); } /** diff --git a/src/main/java/com/jfrog/ide/common/go/GoScanWorkspaceCreator.java b/src/main/java/com/jfrog/ide/common/go/GoScanWorkspaceCreator.java index ba0e65de..63390580 100644 --- a/src/main/java/com/jfrog/ide/common/go/GoScanWorkspaceCreator.java +++ b/src/main/java/com/jfrog/ide/common/go/GoScanWorkspaceCreator.java @@ -4,6 +4,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.jfrog.build.api.util.Log; import org.jfrog.build.extractor.go.GoDriver; +import org.jfrog.build.extractor.WslUtils; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -11,6 +12,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -27,13 +31,16 @@ public class GoScanWorkspaceCreator implements FileVisitor { private final Path sourceDir; private final Path targetDir; private final Log logger; + private final boolean runGoThroughWsl; private static final String[] EXCLUDED_DIRS = new String[]{".git", ".idea", ".vscode"}; - public GoScanWorkspaceCreator(String executablePath, Path sourceDir, Path targetDir, Path goModAbsDir, Map env, Log logger) { - this.goDriver = new GoDriver(executablePath, env, goModAbsDir.toFile(), logger); + public GoScanWorkspaceCreator(String executablePath, Path sourceDir, Path targetDir, Path goModAbsDir, + Map env, Log logger, boolean runGoThroughWsl) { + this.goDriver = new GoDriver(executablePath, env, goModAbsDir.toFile(), logger, runGoThroughWsl); this.sourceDir = sourceDir; this.targetDir = targetDir; this.logger = logger; + this.runGoThroughWsl = runGoThroughWsl; } @Override @@ -73,7 +80,17 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO if (fileName.equals("go.mod")) { Path targetGoMod = targetDir.resolve(sourceDir.relativize(file)); Files.copy(file, targetGoMod); - goDriver.runCmd("run . -goModPath=" + targetGoMod.toAbsolutePath() + " -wd=" + sourceDir.toAbsolutePath(), true); + if (runGoThroughWsl) { + String goModPathArg = WslUtils.windowsLocalPathToWslMount(targetGoMod.toAbsolutePath().toString()); + String sourceAbs = sourceDir.toAbsolutePath().toString(); + String wdArg = WslUtils.isWslPath(sourceAbs) + ? WslUtils.toLinuxPath(sourceAbs) + : WslUtils.windowsLocalPathToWslMount(sourceAbs); + List args = new ArrayList<>(Arrays.asList("run", ".", "-goModPath=" + goModPathArg, "-wd=" + wdArg)); + goDriver.runCmd(args, true); + } else { + goDriver.runCmd("run . -goModPath=" + targetGoMod.toAbsolutePath() + " -wd=" + sourceDir.toAbsolutePath(), true); + } return FileVisitResult.CONTINUE; } // Files other than go.mod and *.go files are not necessary to build the dependency tree of used Go packages. diff --git a/src/main/java/com/jfrog/ide/common/go/GoTreeBuilder.java b/src/main/java/com/jfrog/ide/common/go/GoTreeBuilder.java index 4541bf41..7bc76e64 100644 --- a/src/main/java/com/jfrog/ide/common/go/GoTreeBuilder.java +++ b/src/main/java/com/jfrog/ide/common/go/GoTreeBuilder.java @@ -10,6 +10,7 @@ import org.jfrog.build.client.Version; import org.jfrog.build.extractor.executor.CommandResults; import org.jfrog.build.extractor.go.GoDriver; +import org.jfrog.build.extractor.WslUtils; import java.io.*; import java.nio.file.Files; @@ -126,7 +127,8 @@ private Path createGoWorkspace() throws IOException { Path goModAbsDir = null; try { goModAbsDir = prepareGoModAbs(); - GoScanWorkspaceCreator goScanWorkspaceCreator = new GoScanWorkspaceCreator(executablePath, projectDir, targetDir, goModAbsDir, env, logger); + boolean runGoThroughWsl = WslUtils.isWslPath(projectDir); + GoScanWorkspaceCreator goScanWorkspaceCreator = new GoScanWorkspaceCreator(executablePath, projectDir, targetDir, goModAbsDir, env, logger, runGoThroughWsl); Files.walkFileTree(projectDir, goScanWorkspaceCreator); } finally { if (goModAbsDir != null) { @@ -176,9 +178,10 @@ private static void populateChildren(DepTree depTree, String[] dependenciesGraph public DepTree buildTree() throws IOException { File tmpDir = createGoWorkspace().toFile(); try { - GoDriver goDriver = new GoDriver(executablePath, env, tmpDir, logger); + boolean runGoThroughWsl = WslUtils.isWslPath(projectDir); + GoDriver goDriver = new GoDriver(executablePath, env, tmpDir, logger, runGoThroughWsl); if (!goDriver.isInstalled()) { - throw new IOException("Could not scan the Go project dependencies, because the Go executable is not in the PATH."); + throw new IOException("Could not scan the Go project dependencies, because the Go executable is not in the PATH. [WSL=" + runGoThroughWsl + "]"); } CommandResults versionRes = goDriver.version(false); diff --git a/src/main/java/com/jfrog/ide/common/nodes/subentities/SourceCodeScanType.java b/src/main/java/com/jfrog/ide/common/nodes/subentities/SourceCodeScanType.java index 0a4183ab..0ede9461 100644 --- a/src/main/java/com/jfrog/ide/common/nodes/subentities/SourceCodeScanType.java +++ b/src/main/java/com/jfrog/ide/common/nodes/subentities/SourceCodeScanType.java @@ -5,9 +5,9 @@ public enum SourceCodeScanType { CONTEXTUAL("analyze-applicability"), - SECRETS("JFrog Secrets scanner"), - IAC("JFrog Terraform scanner"), - SAST("JFrog SAST"), + SECRETS("secrets-scan"), + IAC("iac-scan-modules"), + SAST("sast"), SCA("JFrog Xray Scanner"); private final String scannerName; diff --git a/src/main/java/com/jfrog/ide/common/npm/NpmComponentUpdater.java b/src/main/java/com/jfrog/ide/common/npm/NpmComponentUpdater.java index 2e96ddf5..feddf777 100644 --- a/src/main/java/com/jfrog/ide/common/npm/NpmComponentUpdater.java +++ b/src/main/java/com/jfrog/ide/common/npm/NpmComponentUpdater.java @@ -1,12 +1,18 @@ package com.jfrog.ide.common.npm; import com.jfrog.ide.common.updateversion.ComponentUpdater; +import org.jfrog.build.extractor.WslUtils; +import org.apache.commons.lang3.StringUtils; import org.jfrog.build.api.util.Log; +import org.jfrog.build.extractor.executor.CommandExecutor; +import org.jfrog.build.extractor.executor.CommandResults; import org.jfrog.build.extractor.npm.NpmDriver; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Map; /** @@ -19,10 +25,33 @@ public class NpmComponentUpdater extends ComponentUpdater { public static final String NPM_VERSION_DELIMITER = "@"; private final NpmDriver npmDriver; + private final CommandExecutor wslExecutor; + private final boolean isWsl; public NpmComponentUpdater(Path projectDir, Log logger, Map env) { super(projectDir, logger); - this.npmDriver = new NpmDriver(env); + this.isWsl = WslUtils.isWslPath(projectDir); + if (isWsl) { + this.npmDriver = null; + this.wslExecutor = new CommandExecutor("wsl.exe", env); + } else { + this.npmDriver = new NpmDriver(env); + this.wslExecutor = null; + } + } + + /** + * Prefix arguments for {@code wsl.exe} so npm runs in the project directory inside WSL + * (aligned with {@link NpmTreeBuilder}). + */ + private List wslNpmInvocationPrefix() { + String linuxPath = WslUtils.toLinuxPath(projectDir.toString()); + List args = new ArrayList<>(); + args.add("--cd"); + args.add(linuxPath); + args.add("--exec"); + args.add("npm"); + return args; } /** @@ -35,7 +64,21 @@ public NpmComponentUpdater(Path projectDir, Log logger, Map env) @Override public void run(String componentName, String componentVersion) throws IOException { super.run(componentName, componentVersion); - npmDriver.install(projectDir.toFile(), Collections.singletonList(this.componentFullName), this.logger); + if (isWsl) { + List args = new ArrayList<>(wslNpmInvocationPrefix()); + args.add("install"); + args.add(this.componentFullName); + try { + CommandResults res = wslExecutor.exeCommand(null, args, null, logger); + if (!res.isOk()) { + throw new IOException(StringUtils.defaultString(res.getErr()) + StringUtils.defaultString(res.getRes())); + } + } catch (IOException | InterruptedException e) { + throw new IOException("npm install failed via WSL", e); + } + } else { + npmDriver.install(projectDir.toFile(), Collections.singletonList(this.componentFullName), this.logger); + } } @Override @@ -45,6 +88,16 @@ public String getVersionDelimiter() { @Override public boolean isDriverInstalled() { + if (isWsl) { + try { + List args = new ArrayList<>(wslNpmInvocationPrefix()); + args.add("--version"); + CommandResults results = wslExecutor.exeCommand(null, args, null, null); + return results.isOk() && !StringUtils.isBlank(results.getRes()); + } catch (Exception e) { + return false; + } + } return npmDriver.isNpmInstalled(); } diff --git a/src/main/java/com/jfrog/ide/common/npm/NpmTreeBuilder.java b/src/main/java/com/jfrog/ide/common/npm/NpmTreeBuilder.java index 1bf16923..99274158 100644 --- a/src/main/java/com/jfrog/ide/common/npm/NpmTreeBuilder.java +++ b/src/main/java/com/jfrog/ide/common/npm/NpmTreeBuilder.java @@ -2,16 +2,24 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.utils.Utils; +import org.jfrog.build.extractor.WslUtils; +import org.apache.commons.lang3.StringUtils; import org.jfrog.build.api.util.Log; +import org.jfrog.build.extractor.executor.CommandExecutor; +import org.jfrog.build.extractor.executor.CommandResults; import org.jfrog.build.extractor.npm.NpmDriver; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -21,14 +29,24 @@ */ public class NpmTreeBuilder { private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final ObjectReader jsonReader = objectMapper.reader(); private final NpmDriver npmDriver; + private final CommandExecutor wslExecutor; + private final boolean isWsl; private final Path projectDir; private final String descriptorFilePath; public NpmTreeBuilder(Path projectDir, String descriptorFilePath, Map env) { this.projectDir = projectDir; this.descriptorFilePath = descriptorFilePath; - this.npmDriver = new NpmDriver(env); + this.isWsl = WslUtils.isWslPath(projectDir); + if (isWsl) { + this.npmDriver = null; + this.wslExecutor = new CommandExecutor("wsl.exe", env); + } else { + this.npmDriver = new NpmDriver(env); + this.wslExecutor = null; + } } /** @@ -39,15 +57,15 @@ public NpmTreeBuilder(Path projectDir, String descriptorFilePath, Map nodes = new HashMap<>(); String packageId = getPackageId(prodResults); addDepTreeNodes(nodes, prodResults, packageId, "prod"); @@ -57,6 +75,64 @@ public DepTree buildTree(Log logger) throws IOException { return tree; } + /** + * Check whether npm is available, accounting for WSL projects. + * For WSL projects, npm is invoked through {@code wsl.exe}. + */ + private boolean isNpmInstalled() { + if (isWsl) { + try { + List args = wslNpmInvocationPrefix(); + args.add("--version"); + CommandResults results = wslExecutor.exeCommand(null, args, null, null); + return results.isOk() && !StringUtils.isBlank(results.getRes()); + } catch (Exception e) { + return false; + } + } + return npmDriver.isNpmInstalled(); + } + + /** + * Prefix arguments for {@code wsl.exe} so npm runs in the project directory inside WSL + * (same {@code --cd} context as {@link #npmList}). + */ + private List wslNpmInvocationPrefix() { + String linuxPath = WslUtils.toLinuxPath(projectDir.toString()); + List args = new ArrayList<>(); + args.add("--cd"); + args.add(linuxPath); + args.add("--exec"); + args.add("npm"); + return args; + } + + /** + * Run {@code npm ls} and return the parsed JSON output. + * For WSL projects, the command is routed through {@code wsl.exe --cd --exec npm ...}. + */ + private JsonNode npmList(List extraArgs) throws IOException { + if (isWsl) { + List args = new ArrayList<>(wslNpmInvocationPrefix()); + args.add("ls"); + args.add("--json"); + args.add("--all"); + args.addAll(extraArgs); + try { + CommandResults commandRes = wslExecutor.exeCommand(null, args, null, null); + String res = StringUtils.isBlank(commandRes.getRes()) ? "{}" : commandRes.getRes(); + JsonNode results = jsonReader.readTree(res); + if (!commandRes.isOk() && !results.has("problems") && results.isObject()) { + ((ObjectNode) results).put("problems", commandRes.getErr()); + } + return results; + } catch (IOException | InterruptedException e) { + throw new IOException("npm ls failed via WSL", e); + } + } + return npmDriver.list(projectDir.toFile(), extraArgs); + } + private void addDepTreeNodes(Map nodes, JsonNode jsonDep, String depId, String scope) { DepTreeNode depNode; if (nodes.containsKey(depId)) { diff --git a/src/main/java/com/jfrog/ide/common/yarn/YarnComponentUpdater.java b/src/main/java/com/jfrog/ide/common/yarn/YarnComponentUpdater.java index 0bb13f54..4abb0835 100644 --- a/src/main/java/com/jfrog/ide/common/yarn/YarnComponentUpdater.java +++ b/src/main/java/com/jfrog/ide/common/yarn/YarnComponentUpdater.java @@ -1,6 +1,7 @@ package com.jfrog.ide.common.yarn; import com.jfrog.ide.common.updateversion.ComponentUpdater; +import org.jfrog.build.extractor.WslUtils; import org.jfrog.build.api.util.Log; import java.io.IOException; @@ -14,7 +15,7 @@ public class YarnComponentUpdater extends ComponentUpdater { public YarnComponentUpdater(Path projectDir, Log logger, Map env) { super(projectDir, logger); - this.yarnDriver = new YarnDriver(env); + this.yarnDriver = new YarnDriver(env, logger, WslUtils.isWslPath(projectDir)); } /** @@ -37,7 +38,7 @@ public String getVersionDelimiter() { @Override public boolean isDriverInstalled() { - return yarnDriver.isYarnInstalled(); + return yarnDriver.isYarnInstalled(projectDir.toFile()); } @Override diff --git a/src/main/java/com/jfrog/ide/common/yarn/YarnDriver.java b/src/main/java/com/jfrog/ide/common/yarn/YarnDriver.java index d2d71d0d..871bbc17 100644 --- a/src/main/java/com/jfrog/ide/common/yarn/YarnDriver.java +++ b/src/main/java/com/jfrog/ide/common/yarn/YarnDriver.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; +import org.jfrog.build.extractor.WslUtils; import org.apache.commons.lang3.StringUtils; import org.jfrog.build.api.util.Log; import org.jfrog.build.api.util.NullLog; @@ -12,7 +13,6 @@ import java.io.File; import java.io.IOException; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -22,20 +22,41 @@ public class YarnDriver { private static final ObjectReader jsonReader = new ObjectMapper().reader(); private final CommandExecutor commandExecutor; private final Log log; + private final boolean useWsl; public YarnDriver(Map env) { - this(env, new NullLog()); + this(env, new NullLog(), false); } public YarnDriver(Map env, Log log) { - this.commandExecutor = new CommandExecutor("yarn", env); + this(env, log, false); + } + + public YarnDriver(Map env, Log log, boolean useWsl) { + this.useWsl = useWsl; + this.commandExecutor = useWsl ? new CommandExecutor("wsl.exe", env) : new CommandExecutor("yarn", env); this.log = log; } + /** + * @return whether Yarn commands are executed via {@code wsl.exe} (WSL UNC project path). + */ + public boolean runsThroughWsl() { + return useWsl; + } + @SuppressWarnings("unused") public boolean isYarnInstalled() { + return isYarnInstalled(null); + } + + /** + * @param projectWorkingDirectory project root, or {@code null} for a global Yarn check (non-WSL only; + * WSL mode should pass the project directory so the check uses the same {@code --cd} as scans). + */ + public boolean isYarnInstalled(File projectWorkingDirectory) { try { - return !version(null).isEmpty(); + return !version(projectWorkingDirectory).isEmpty(); } catch (IOException | InterruptedException e) { return false; } @@ -128,8 +149,20 @@ private CommandResults runCommand(File workingDirectory, String[] args) throws I } private CommandResults runCommand(File workingDirectory, String[] args, List extraArgs) throws IOException, InterruptedException { - List finalArgs = Stream.concat(Arrays.stream(args), extraArgs.stream()).collect(Collectors.toList()); - CommandResults commandRes = commandExecutor.exeCommand(workingDirectory, finalArgs, null, null); + List finalArgs = new ArrayList<>(); + File wdForExecutor = workingDirectory; + if (useWsl) { + // Route through wsl.exe. If a working directory is given, convert it to a Linux path via --cd. + if (workingDirectory != null) { + finalArgs.add("--cd"); + finalArgs.add(WslUtils.toLinuxPath(workingDirectory.getPath())); + } + finalArgs.add("--exec"); + finalArgs.add("yarn"); + wdForExecutor = null; // Working directory is handled by --cd above + } + Stream.concat(Arrays.stream(args), extraArgs.stream()).forEach(finalArgs::add); + CommandResults commandRes = commandExecutor.exeCommand(wdForExecutor, finalArgs, null, null); if (!commandRes.isOk()) { throw new IOException(commandRes.getErr() + commandRes.getRes()); } diff --git a/src/main/java/com/jfrog/ide/common/yarn/YarnTreeBuilder.java b/src/main/java/com/jfrog/ide/common/yarn/YarnTreeBuilder.java index 5736991a..03a8514a 100644 --- a/src/main/java/com/jfrog/ide/common/yarn/YarnTreeBuilder.java +++ b/src/main/java/com/jfrog/ide/common/yarn/YarnTreeBuilder.java @@ -5,6 +5,7 @@ import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; import com.jfrog.ide.common.nodes.subentities.ImpactTree; +import org.jfrog.build.extractor.WslUtils; import org.apache.commons.lang3.StringUtils; import org.jfrog.build.api.util.Log; @@ -29,7 +30,7 @@ public class YarnTreeBuilder { public YarnTreeBuilder(Path projectDir, String descriptorFilePath, Map env, Log log) { this.projectDir = projectDir; this.descriptorFilePath = descriptorFilePath; - this.yarnDriver = new YarnDriver(env, log); + this.yarnDriver = new YarnDriver(env, log, WslUtils.isWslPath(projectDir)); } /** @@ -39,8 +40,8 @@ public YarnTreeBuilder(Path projectDir, String descriptorFilePath, Map