diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9671ea9c..ad008dc6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,6 @@ jobs: steps: - uses: actions/checkout@v4 with: - submodules: true ref: ${{ github.event.pull_request.head.sha }} - name: Unlabel 'safe to test' diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index bcff49ec..00000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "src/main/resources/gomod-absolutizer"] - path = src/main/resources/gomod-absolutizer - url = https://github.com/jfrog/gomod-absolutizer - branch = main diff --git a/README.md b/README.md index 2c67d60d..d6d38814 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,6 @@ This project includes the common code used by the [JFrog Idea Plugin](https://github.com/jfrog/jfrog-idea-plugin) and the [JFrog Eclipse plugin](https://github.com/jfrog/jfrog-eclipse-plugin). # Building and Testing the Sources -After cloning the project, update submodules: -``` -git submodule init -git submodule update -``` To build the code using the Gradle wrapper in Linux/Unix run: ``` > ./gradlew clean build diff --git a/build.gradle b/build.gradle index a538b304..4c3c4794 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ repositories { } def buildInfoVersion = '2.43.9' -def jacksonVersion = '2.18.6' +def jacksonVersion = '2.21.7' dependencies { implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: jacksonVersion diff --git a/release/pipelines.release.yml b/release/pipelines.release.yml deleted file mode 100644 index f33e1170..00000000 --- a/release/pipelines.release.yml +++ /dev/null @@ -1,83 +0,0 @@ -pipelines: - - name: release_ide_plugins_common - configuration: - runtime: - type: image - image: - auto: - language: java - versions: - - "17" - environmentVariables: - readOnly: - NEXT_VERSION: 0.0.0 - NEXT_DEVELOPMENT_VERSION: 0.0.x-SNAPSHOT - BRANCH_NAME: master - - steps: - - name: Release - type: Bash - configuration: - inputResources: - - name: idePluginsJavaReleaseGit - integrations: - - name: il_automation - - name: ecosys_entplus_deployer - execution: - onExecute: - - cd $res_idePluginsJavaReleaseGit_resourcePath - - # Fetch submodule files - - git submodule init - - git submodule update - - # Set env - - export CI=true - - export JFROG_BUILD_STATUS=PASS - - export JFROG_CLI_BUILD_NAME=ecosystem-ide-plugins-common-release - - export JFROG_CLI_BUILD_NUMBER=$run_number - - export JFROG_CLI_BUILD_PROJECT=ecosys - - # Configure git - - if [[ $NEXT_VERSION =~ ^1.* ]]; then BRANCH="v1"; else BRANCH=$BRANCH_NAME; fi - - git checkout ${BRANCH} - - git remote set-url origin https://$int_il_automation_token@github.com/jfrog/ide-plugins-common.git - - # Make sure versions provided - - echo "Checking variables" - - test -n "$NEXT_VERSION" -a "$NEXT_VERSION" != "0.0.0" - - test -n "$NEXT_DEVELOPMENT_VERSION" -a "$NEXT_DEVELOPMENT_VERSION" != "0.0.x-SNAPSHOT" - - # Configure JFrog CLI - - curl -fL https://install-cli.jfrog.io | sh - - jf c rm --quiet - - jf c add internal --url=$int_ecosys_entplus_deployer_url --user=$int_ecosys_entplus_deployer_user --password=$int_ecosys_entplus_deployer_apikey - - jf gradlec --use-wrapper --repo-deploy ecosys-oss-release-local --deploy-maven-desc - - # Run audit - #- jf audit --gradle --use-wrapper - - # Update version - - sed -i "s/\(version=\).*\$/\1${NEXT_VERSION}/" gradle.properties - - git commit -am "[artifactory-release] Release version ${NEXT_VERSION} [skipRun]" --allow-empty - - git tag ${NEXT_VERSION} - - # Run build and publish - - > - env -i PATH=$PATH HOME=$HOME - JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME - JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER - JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT - jf gradle clean build -x test artifactoryPublish - - jf rt bag && jf rt bce - - jf rt bp - - # Distribute release bundle - - jf ds rbc ecosystem-ide-plugins-common $NEXT_VERSION --spec=./release/specs/prod-rbc-filespec.json --spec-vars="version=$NEXT_VERSION" --sign - - jf ds rbd ecosystem-ide-plugins-common $NEXT_VERSION --site="releases.jfrog.io" --sync - - # Update next development version - - sed -i "s/\(version=\).*\$/\1${NEXT_DEVELOPMENT_VERSION}/" gradle.properties - - git commit -am "[artifactory-release] Next development version [skipRun]" - - git push - - git push --tags diff --git a/release/pipelines.resources.yml b/release/pipelines.resources.yml deleted file mode 100644 index a4cb4f82..00000000 --- a/release/pipelines.resources.yml +++ /dev/null @@ -1,20 +0,0 @@ -resources: - - name: idePluginsJavaSnapshotGit - type: GitRepo - configuration: - path: jfrog/ide-plugins-common - gitProvider: il_automation - buildOn: - pullRequestCreate: true - branches: - include: master - cancelPendingRunsOn: - pullRequestUpdate: true - - - name: idePluginsJavaReleaseGit - type: GitRepo - configuration: - path: jfrog/ide-plugins-common - gitProvider: il_automation - buildOn: - commit: false diff --git a/release/pipelines.snapshot.yml b/release/pipelines.snapshot.yml deleted file mode 100644 index a322e8ac..00000000 --- a/release/pipelines.snapshot.yml +++ /dev/null @@ -1,72 +0,0 @@ -pipelines: - - name: build_ide_plugins_common_snapshot - configuration: - runtime: - type: image - image: - auto: - language: java - versions: - - "17" - - steps: - - name: Snapshot - type: Bash - configuration: - inputResources: - - name: idePluginsJavaSnapshotGit - integrations: - - name: ecosys_entplus_deployer - execution: - onStart: - - restore_cache_files gradle_cache $res_idePluginsJavaSnapshotGit_resourcePath/.gradle - onExecute: - - cd $res_idePluginsJavaSnapshotGit_resourcePath - # Install Go - - curl -OL https://golang.org/dl/go1.17.6.linux-amd64.tar.gz - - tar -C /usr/local -xzf go1.17.6.linux-amd64.tar.gz - - export PATH=$PATH:/usr/local/go/bin - - go version - # Fetch submodule files - - git submodule init - - git submodule update - - # Set env - - export CI=true - - export JFROG_BUILD_STATUS=PASS - - export JFROG_CLI_BUILD_NAME=ecosystem-ide-plugins-common-dev - - export JFROG_CLI_BUILD_NUMBER=$run_number - - export JFROG_CLI_BUILD_PROJECT=ecosys - - # Configure JFrog CLI - - curl -fL https://install-cli.jfrog.io | sh - - jf c rm --quiet - - jf c add internal --url=$int_ecosys_entplus_deployer_url --user=$int_ecosys_entplus_deployer_user --password=$int_ecosys_entplus_deployer_apikey - - jf gradlec --use-wrapper --repo-deploy ecosys-oss-snapshot-local --deploy-maven-desc - - # Run audit - - jf audit - - # Delete former snapshots to make sure the release bundle will not contain the same artifacts - - jf rt del "ecosys-oss-snapshot-local/com/jfrog/ide/ide-plugins-common/*" --quiet - - # Run test, build and publish snapshot - - > - env -i PATH=$PATH HOME=$HOME - JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME - JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER - JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT - jf gradle clean build -x test artifactoryPublish - - jf rt bag && jf rt bce - - jf rt bp - - # Distribute release bundle - - jf ds rbc ecosystem-ide-plugins-common-snapshot $run_number --spec=./release/specs/dev-rbc-filespec.json --sign - - jf ds rbd ecosystem-ide-plugins-common-snapshot $run_number --site="releases.jfrog.io" --sync - - onComplete: - # Show tests in the *Tests* tab - - save_tests $res_idePluginsJavaSnapshotGit_resourcePath/build/test-results/test/ - - # Save gradle cache - - add_cache_files $res_idePluginsJavaSnapshotGit_resourcePath/.gradle gradle_cache diff --git a/release/specs/dev-rbc-filespec.json b/release/specs/dev-rbc-filespec.json deleted file mode 100644 index edfe436a..00000000 --- a/release/specs/dev-rbc-filespec.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "files": [ - { - "pattern": "ecosys-oss-snapshot-local/(com/jfrog/ide/ide-plugins-common/*/ide-plugins-common-*)", - "target": "oss-snapshot-local/{1}" - } - ] -} diff --git a/release/specs/prod-rbc-filespec.json b/release/specs/prod-rbc-filespec.json deleted file mode 100644 index 6f202c11..00000000 --- a/release/specs/prod-rbc-filespec.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "files": [ - { - "pattern": "ecosys-oss-release-local/(com/jfrog/ide/ide-plugins-common/${version}/ide-plugins-common-*)", - "target": "oss-release-local/{1}" - } - ] -} diff --git a/src/main/java/com/jfrog/ide/common/go/GoScanWorkspaceCreator.java b/src/main/java/com/jfrog/ide/common/go/GoScanWorkspaceCreator.java deleted file mode 100644 index 63390580..00000000 --- a/src/main/java/com/jfrog/ide/common/go/GoScanWorkspaceCreator.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.jfrog.ide.common.go; - -import org.apache.commons.lang3.StringUtils; -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; -import java.nio.file.FileVisitor; -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; - -/** - * This FileVisitor copies all go.mod and *.go files from the input source directory to the input target directory. - * For all go.mod files, replaces relative paths to absolute. - * That functionality is needed, so that we can calculate the go dependencies tree on a copy of the original code project, - * rather than on the original one, in order to avoid changing it. - * - * @author yahavi - **/ -public class GoScanWorkspaceCreator implements FileVisitor { - private final GoDriver goDriver; - 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, 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 - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - // Skip excluded directories. - if (StringUtils.equalsAny(dir.getFileName().toString(), EXCLUDED_DIRS)) { - return FileVisitResult.SKIP_SUBTREE; - } - // Skip subdirectories with go.mod files. - // These directories are different Go projects and their go files should not be in the root project. - if (!sourceDir.equals(dir)) { - try (Stream files = Files.list(dir)) { - if (files.anyMatch(file -> file.getFileName().toString().equals("go.mod"))) { - return FileVisitResult.SKIP_SUBTREE; - } - } - } - - Path resolve = targetDir.resolve(sourceDir.relativize(dir)); - if (Files.notExists(resolve)) { - Files.createDirectories(resolve); - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String fileName = file.getFileName().toString(); - - // Go files should be copied to allow running `go list -f "{{with .Module}}{{.Path}} {{.Version}}{{end}}" all` - // and to get the list package that are actually in use by the Go project. - if (fileName.endsWith(".go")) { - Files.copy(file, targetDir.resolve(sourceDir.relativize(file))); - return FileVisitResult.CONTINUE; - } - // Copy the root go.mod file and replace relative path in "replace" to absolute paths. - if (fileName.equals("go.mod")) { - Path targetGoMod = targetDir.resolve(sourceDir.relativize(file)); - Files.copy(file, targetGoMod); - 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. - // Therefore, we just create an empty file with the same name so go:embed files won't cause a missing file error. - if (!fileName.equals("go.sum")) { - Files.createFile(targetDir.resolve(sourceDir.relativize(file))); - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) { - if (exc != null) { - logger.warn("An error occurred during preparing Go workspace " + ExceptionUtils.getRootCauseMessage(exc)); - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) { - if (exc != null) { - logger.warn("An error occurred during preparing Go workspace " + ExceptionUtils.getRootCauseMessage(exc)); - return FileVisitResult.SKIP_SUBTREE; - } - return FileVisitResult.CONTINUE; - } -} 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 7bc76e64..86e10d03 100644 --- a/src/main/java/com/jfrog/ide/common/go/GoTreeBuilder.java +++ b/src/main/java/com/jfrog/ide/common/go/GoTreeBuilder.java @@ -17,6 +17,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -27,8 +28,6 @@ @SuppressWarnings({"unused"}) public class GoTreeBuilder { - // Required files of the gomod-absolutizer Go program - private static final String[] GO_MOD_ABS_COMPONENTS = new String[]{"go.mod", "go.sum", "main.go", "utils.go"}; private static final Version MIN_GO_VERSION_FOR_BUILD_VCS_FLAG = new Version("1.18"); public static final String GO_VERSION_PATTERN = "^go(\\d*.\\d*.*\\d*)"; private static final String GO_SOURCE_CODE_PREFIX = "github.com/golang/go:"; @@ -116,46 +115,41 @@ static Version parseGoVersion(CommandResults versionRes, Log logger) { } /** - * Copy go.mod file to a temporary directory. - * This is necessary to bypass checksum mismatches issues in the original go.sum. + * Returns the environment for running go in the project directory, with go reading and writing a copy of the + * project's go.mod, and a go.sum next to it, in the given directory instead of the project's own files. * - * @return the temporary directory. - * @throws IOException in case of any I/O error. + * @param goFlags the GOFLAGS go already uses, including values saved with "go env -w" */ - private Path createGoWorkspace() throws IOException { - Path targetDir = Files.createTempDirectory(null); - Path goModAbsDir = null; - try { - goModAbsDir = prepareGoModAbs(); - boolean runGoThroughWsl = WslUtils.isWslPath(projectDir); - GoScanWorkspaceCreator goScanWorkspaceCreator = new GoScanWorkspaceCreator(executablePath, projectDir, targetDir, goModAbsDir, env, logger, runGoThroughWsl); - Files.walkFileTree(projectDir, goScanWorkspaceCreator); - } finally { - if (goModAbsDir != null) { - FileUtils.deleteQuietly(goModAbsDir.toFile()); - } + private Map createScanEnv(Path tmpDir, String goFlags, boolean runGoThroughWsl) throws IOException { + Path goMod = Files.copy(projectDir.resolve("go.mod"), tmpDir.resolve("go.mod")); + String goModPath = runGoThroughWsl ? WslUtils.toWslLinuxCdPath(goMod.toFile()) : goMod.toString(); + Map scanEnv = env == null ? new HashMap<>() : new HashMap<>(env); + scanEnv.put("GOFLAGS", StringUtils.trim(goFlags + " " + toGoFlagsEntry("-modfile=" + goModPath))); + // A go.work in or above the project puts go in workspace mode, which does not allow -modfile. + scanEnv.put("GOWORK", "off"); + if (runGoThroughWsl) { + // Windows environment variables reach go inside WSL only when WSLENV lists them. They are listed without a + // path translation flag, since the -modfile path is already a Linux path. + String wslEnv = StringUtils.defaultString(scanEnv.getOrDefault("WSLENV", System.getenv("WSLENV"))); + List wslEnvEntries = Arrays.stream(wslEnv.split(":")) + .filter(entry -> StringUtils.isNotBlank(entry) && !StringUtils.equalsAny(StringUtils.substringBefore(entry, "/"), "GOFLAGS", "GOWORK")) + .collect(Collectors.toList()); + wslEnvEntries.addAll(List.of("GOFLAGS", "GOWORK")); + scanEnv.put("WSLENV", String.join(":", wslEnvEntries)); } - return targetDir; + return scanEnv; } /** - * Copy gomod-absolutizer Go files to a temp directory. - * The gomod-absolutizer is used to change relative paths in go.mod files to absolute paths. - * - * @throws IOException in case of any I/O error. + * Returns a flag as a GOFLAGS entry. GOFLAGS is split on spaces, so a flag with a space is quoted, which Go 1.21 + * and later support. */ - private Path prepareGoModAbs() throws IOException { - Path goModAbsDir = Files.createTempDirectory(null); - for (String fileName : GO_MOD_ABS_COMPONENTS) { - try (InputStream is = getClass().getResourceAsStream("/gomod-absolutizer/" + fileName); - OutputStream os = new FileOutputStream(goModAbsDir.resolve(fileName).toFile())) { - if (is == null) { - throw new IOException("Couldn't find resource /gomod-absolutizer/" + fileName); - } - is.transferTo(os); - } + static String toGoFlagsEntry(String flag) { + if (!StringUtils.containsWhitespace(flag)) { + return flag; } - return goModAbsDir; + String quote = flag.contains("'") ? "\"" : "'"; + return quote + flag + quote; } private static void populateChildren(DepTree depTree, String[] dependenciesGraph) { @@ -176,23 +170,25 @@ private static void populateChildren(DepTree depTree, String[] dependenciesGraph } public DepTree buildTree() throws IOException { - File tmpDir = createGoWorkspace().toFile(); + Path tmpDir = Files.createTempDirectory(null); try { boolean runGoThroughWsl = WslUtils.isWslPath(projectDir); - GoDriver goDriver = new GoDriver(executablePath, env, tmpDir, logger, runGoThroughWsl); + GoDriver goDriver = new GoDriver(executablePath, env, projectDir.toFile(), logger, runGoThroughWsl); if (!goDriver.isInstalled()) { 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); Version goVersion = parseGoVersion(versionRes, logger); - goDriver.modTidy(false, goVersion.isAtLeast(MIN_GO_VERSION)); - DepTree depTree = createDependencyTree(goDriver, logger, false, goVersion.isAtLeast(MIN_GO_VERSION_FOR_BUILD_VCS_FLAG)); + String goFlags = goDriver.runCmd("env GOFLAGS", false).getRes().trim(); + GoDriver scanDriver = new GoDriver(executablePath, createScanEnv(tmpDir, goFlags, runGoThroughWsl), projectDir.toFile(), logger, runGoThroughWsl); + scanDriver.modTidy(false, goVersion.isAtLeast(MIN_GO_VERSION)); + DepTree depTree = createDependencyTree(scanDriver, logger, false, goVersion.isAtLeast(MIN_GO_VERSION_FOR_BUILD_VCS_FLAG)); addGoVersionNode(depTree, goVersion); depTree.getRootNode().descriptorFilePath(descriptorFilePath); return depTree; } finally { - FileUtils.deleteDirectory(tmpDir); + FileUtils.deleteDirectory(tmpDir.toFile()); } } diff --git a/src/main/resources/gomod-absolutizer b/src/main/resources/gomod-absolutizer deleted file mode 160000 index 2bf19fd6..00000000 --- a/src/main/resources/gomod-absolutizer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2bf19fd6b65b1d5d7f3a88673216d836e3583806 diff --git a/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java b/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java index b46d03ee..753691cd 100644 --- a/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java +++ b/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java @@ -26,6 +26,7 @@ import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; import java.util.*; +import java.util.stream.Collectors; import static org.testng.Assert.*; @@ -39,6 +40,7 @@ public class JfrogCliDriverTest { private final SimpleDateFormat timeStampFormat = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); private final Map testEnv = new HashMap<>(); private JfrogCliDriver jfrogCliDriver; + private static final Set MAVEN_EXAMPLE_VULNERABILITIES = Set.of("CVE-2017-9801", "CVE-2018-1294", "CVE-2021-29425"); private final String PASSWORD = System.getenv("JF_CLI_TEST_PASSWORD"); private final String USER_NAME = System.getenv("JF_CLI_TEST_USER"); private final String SERVER_URL = System.getenv("JF_CLI_TEST_URL"); @@ -243,7 +245,8 @@ public void testRunAudit_MultiMavenProject() { // Verify the findings FileTreeNode node = findings.stream().filter(finding -> finding.getTitle().equals("pom.xml")).findFirst().orElse(null); assertNotNull(node, "Expected SCA findings in pom.xml"); - assertEquals(node.getChildren().size(), 3, "Expected exactly three vulnerabilities"); + Set vulnerabilities = node.getChildren().stream().map(child -> ((FileIssueNode) child).getTitle()).collect(Collectors.toSet()); + assertTrue(vulnerabilities.containsAll(MAVEN_EXAMPLE_VULNERABILITIES), "Expected " + MAVEN_EXAMPLE_VULNERABILITIES + ", found " + vulnerabilities); assertEquals(node.getSeverity(), Severity.High, "Expected severity to be HIGH"); FileIssueNode issue = (FileIssueNode) node.getChildren().get(0); assertEquals(issue.getReporterType(), SourceCodeScanType.SCA, "Expected reporter type to be SCA"); @@ -286,7 +289,8 @@ public void testRunAudit_WithExcludedPattern() { // Verify the findings FileTreeNode node = findings.stream().filter(finding -> finding.getTitle().equals("pom.xml")).findFirst().orElse(null); assertNotNull(node, "Expected SCA findings in pom.xml"); - assertEquals(node.getChildren().size(), 3, "Expected exactly three vulnerabilities"); + Set vulnerabilities = node.getChildren().stream().map(child -> ((FileIssueNode) child).getTitle()).collect(Collectors.toSet()); + assertTrue(vulnerabilities.containsAll(MAVEN_EXAMPLE_VULNERABILITIES), "Expected " + MAVEN_EXAMPLE_VULNERABILITIES + ", found " + vulnerabilities); assertEquals(node.getSeverity(), Severity.High, "Expected severity to be HIGH"); FileIssueNode issue = (FileIssueNode) node.getChildren().get(0); assertEquals(issue.getReporterType(), SourceCodeScanType.SCA, "Expected reporter type to be SCA"); diff --git a/src/test/java/com/jfrog/ide/common/go/GoTreeBuilderTest.java b/src/test/java/com/jfrog/ide/common/go/GoTreeBuilderTest.java index 9ddf55ab..bbd1848c 100644 --- a/src/test/java/com/jfrog/ide/common/go/GoTreeBuilderTest.java +++ b/src/test/java/com/jfrog/ide/common/go/GoTreeBuilderTest.java @@ -2,6 +2,7 @@ import com.jfrog.ide.common.deptree.DepTree; import com.jfrog.ide.common.deptree.DepTreeNode; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jfrog.build.api.util.Log; import org.jfrog.build.api.util.NullLog; @@ -12,6 +13,7 @@ import org.testng.annotations.Test; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; @@ -42,9 +44,12 @@ public void testCreateDependencyTree1() { try { Path projectDir = GO_ROOT.resolve("project1"); + String goMod = Files.readString(projectDir.resolve("go.mod")); GoTreeBuilder treeBuilder = new GoTreeBuilder(null, projectDir, projectDir.resolve("go.mod").toString(), null, log); DepTree dt = treeBuilder.buildTree(); validateDependencyTreeResults(expected, dt); + assertEquals(Files.readString(projectDir.resolve("go.mod")), goMod); + assertFalse(Files.exists(projectDir.resolve("go.sum"))); } catch (IOException ex) { fail(ExceptionUtils.getStackTrace(ex)); } @@ -60,9 +65,13 @@ public void testCreateDependencyTree2() { }}; try { Path projectDir = GO_ROOT.resolve("project2"); + byte[] goMod = Files.readAllBytes(projectDir.resolve("go.mod")); + byte[] goSum = Files.readAllBytes(projectDir.resolve("go.sum")); GoTreeBuilder treeBuilder = new GoTreeBuilder(null, projectDir, projectDir.resolve("go.mod").toString(), null, log); DepTree dt = treeBuilder.buildTree(); validateDependencyTreeResults(expected, dt); + assertEquals(Files.readAllBytes(projectDir.resolve("go.mod")), goMod); + assertEquals(Files.readAllBytes(projectDir.resolve("go.sum")), goSum); } catch (IOException ex) { fail(ExceptionUtils.getStackTrace(ex)); } @@ -106,6 +115,40 @@ public void testCreateDependencyTree4() { } } + /** + * Go reads a -modfile path that contains a space from GOFLAGS. + */ + @Test + public void testGoFlagsEntryWithSpace() throws IOException { + Path modFileDir = Files.createTempDirectory("go mod file"); + try { + Path goMod = Files.copy(GO_ROOT.resolve("project5").resolve("go.mod"), modFileDir.resolve("go.mod")); + Map env = Map.of("GOFLAGS", GoTreeBuilder.toGoFlagsEntry("-modfile=" + goMod), "GOWORK", "off"); + GoDriver driver = new GoDriver(null, env, GO_ROOT.resolve("project5").toFile(), log); + assertEquals(driver.runCmd("list -m", false).getRes().trim(), "project5"); + } finally { + FileUtils.deleteDirectory(modFileDir.toFile()); + } + } + + /** + * The project is a module of a go.work workspace. + */ + @Test + public void testCreateDependencyTreeInWorkspace() throws IOException { + Path workspaceDir = Files.createTempDirectory("goWorkspace"); + try { + Path projectDir = workspaceDir.resolve("project5"); + FileUtils.copyDirectory(GO_ROOT.resolve("project5").toFile(), projectDir.toFile()); + Files.writeString(workspaceDir.resolve("go.work"), "go 1.21\n\nuse ./project5\n"); + GoTreeBuilder treeBuilder = new GoTreeBuilder(null, projectDir, projectDir.resolve("go.mod").toString(), null, log); + DepTree dt = treeBuilder.buildTree(); + validateDependencyTreeResults(new HashMap<>(), dt); + } finally { + FileUtils.deleteDirectory(workspaceDir.toFile()); + } + } + /** * The project has no dependencies. */