aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce-common/src/test/java/org/sonar/ce/common/sca/ScaHolderImplTest.java1
-rw-r--r--server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaDependenciesDaoIT.java4
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java5
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfBuilder.java8
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/sca/ListOfListOfStringsTypeHandler.java60
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyDto.java23
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyReleaseDto.java32
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaDependenciesMapper.xml4
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyDtoTest.java3
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyReleaseDtoTest.java2
-rw-r--r--server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaDependenciesDbTester.java2
11 files changed, 123 insertions, 21 deletions
diff --git a/server/sonar-ce-common/src/test/java/org/sonar/ce/common/sca/ScaHolderImplTest.java b/server/sonar-ce-common/src/test/java/org/sonar/ce/common/sca/ScaHolderImplTest.java
index b60857e494a..175945e1980 100644
--- a/server/sonar-ce-common/src/test/java/org/sonar/ce/common/sca/ScaHolderImplTest.java
+++ b/server/sonar-ce-common/src/test/java/org/sonar/ce/common/sca/ScaHolderImplTest.java
@@ -70,6 +70,7 @@ class ScaHolderImplTest {
"compile",
"some/path",
"another/path",
+ List.of(List.of("pkg:npm/foo@1.0.0")),
1L,
2L);
}
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaDependenciesDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaDependenciesDaoIT.java
index bad13686383..23cd5d864b4 100644
--- a/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaDependenciesDaoIT.java
+++ b/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaDependenciesDaoIT.java
@@ -47,7 +47,6 @@ class ScaDependenciesDaoIT {
List<Map<String, Object>> select = db.select(db.getSession(), "select * from sca_dependencies");
assertThat(select).hasSize(1);
Map<String, Object> stringObjectMap = select.get(0);
- stringObjectMap.remove("chains");
assertThat(stringObjectMap).containsExactlyInAnyOrderEntriesOf(
Map.ofEntries(
Map.entry("uuid", scaDependencyDto.uuid()),
@@ -56,6 +55,7 @@ class ScaDependenciesDaoIT {
Map.entry("scope", scaDependencyDto.scope()),
Map.entry("user_dependency_file_path", scaDependencyDto.userDependencyFilePath()),
Map.entry("lockfile_dependency_file_path", scaDependencyDto.lockfileDependencyFilePath()),
+ Map.entry("chains", scaDependencyDto.getChainsJson()),
Map.entry("created_at", scaDependencyDto.createdAt()),
Map.entry("updated_at", scaDependencyDto.updatedAt())));
}
@@ -210,7 +210,6 @@ class ScaDependenciesDaoIT {
List<Map<String, Object>> select = db.select(db.getSession(), "select * from sca_dependencies");
assertThat(select).hasSize(1);
Map<String, Object> stringObjectMap = select.get(0);
- stringObjectMap.remove("chains");
assertThat(stringObjectMap).containsExactlyInAnyOrderEntriesOf(
Map.ofEntries(
Map.entry("uuid", updatedScaDependency.uuid()),
@@ -219,6 +218,7 @@ class ScaDependenciesDaoIT {
Map.entry("scope", updatedScaDependency.scope()),
Map.entry("user_dependency_file_path", updatedScaDependency.userDependencyFilePath()),
Map.entry("lockfile_dependency_file_path", updatedScaDependency.lockfileDependencyFilePath()),
+ Map.entry("chains", updatedScaDependency.getChainsJson()),
Map.entry("created_at", updatedScaDependency.createdAt()),
Map.entry("updated_at", updatedScaDependency.updatedAt())));
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
index cd147772d29..92dd2215aaa 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
@@ -151,6 +151,7 @@ import org.sonar.db.rule.RuleChangeMapper;
import org.sonar.db.rule.RuleMapper;
import org.sonar.db.rule.RuleParamDto;
import org.sonar.db.rule.RuleRepositoryMapper;
+import org.sonar.db.sca.ListOfListOfStringsTypeHandler;
import org.sonar.db.sca.ScaDependenciesMapper;
import org.sonar.db.sca.ScaDependencyDto;
import org.sonar.db.sca.ScaReleasesMapper;
@@ -205,6 +206,9 @@ public class MyBatis {
MyBatisConfBuilder confBuilder = new MyBatisConfBuilder(database);
+ // Custom column type handlers, keep them sorted alphabetically
+ confBuilder.loadTypeHandler(ListOfListOfStringsTypeHandler.class);
+
// DTO aliases, keep them sorted alphabetically
confBuilder.loadAlias("ActiveRule", ActiveRuleDto.class);
confBuilder.loadAlias("ActiveRuleParam", ActiveRuleParamDto.class);
@@ -220,6 +224,7 @@ public class MyBatis {
confBuilder.loadAlias("GithubOrganizationGroup", GithubOrganizationGroupDto.class);
confBuilder.loadAlias("FilePathWithHash", FilePathWithHashDto.class);
confBuilder.loadAlias("KeyWithUuid", KeyWithUuidDto.class);
+ confBuilder.loadAlias("ListOfListOfStringsTypeHandler", ListOfListOfStringsTypeHandler.class);
confBuilder.loadAlias("Group", GroupDto.class);
confBuilder.loadAlias("GroupMembership", GroupMembershipDto.class);
confBuilder.loadAlias("GroupPermission", GroupPermissionDto.class);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfBuilder.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfBuilder.java
index a9efb454c14..928e5982c42 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfBuilder.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfBuilder.java
@@ -51,11 +51,15 @@ class MyBatisConfBuilder {
this.conf.setLocalCacheScope(LocalCacheScope.STATEMENT);
}
- void loadAlias(String alias, Class dtoClass) {
+ void loadTypeHandler(Class<?> typeHandlerClass) {
+ this.conf.getTypeHandlerRegistry().register(typeHandlerClass);
+ }
+
+ void loadAlias(String alias, Class<?> dtoClass) {
conf.getTypeAliasRegistry().registerAlias(alias, dtoClass);
}
- void loadMapper(Class mapperClass) {
+ void loadMapper(Class<?> mapperClass) {
String configFile = configFilePath(mapperClass);
InputStream input = null;
try {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ListOfListOfStringsTypeHandler.java b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ListOfListOfStringsTypeHandler.java
new file mode 100644
index 00000000000..c9b5c309647
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ListOfListOfStringsTypeHandler.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2025 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.sca;
+
+import org.apache.ibatis.type.BaseTypeHandler;
+import org.apache.ibatis.type.JdbcType;
+import com.google.gson.Gson;
+import java.util.List;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.ResultSet;
+import java.sql.CallableStatement;
+import org.apache.ibatis.type.MappedJdbcTypes;
+import org.apache.ibatis.type.MappedTypes;
+
+@MappedJdbcTypes(JdbcType.CLOB)
+@MappedTypes(List.class)
+public class ListOfListOfStringsTypeHandler extends BaseTypeHandler<List<List<String>>> {
+ private static final Gson GSON = new Gson();
+ private static final Type type = new TypeToken<List<List<String>>>() {}.getType();
+
+ @Override
+ public void setNonNullParameter(PreparedStatement ps, int i, List<List<String>> parameter, JdbcType jdbcType) throws SQLException {
+ ps.setString(i, GSON.toJson(parameter));
+ }
+
+ @Override
+ public List<List<String>> getNullableResult(ResultSet rs, String columnName) throws SQLException {
+ return GSON.fromJson(rs.getString(columnName), type);
+ }
+
+ @Override
+ public List<List<String>> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+ return GSON.fromJson(rs.getString(columnIndex), type);
+ }
+
+ @Override
+ public List<List<String>> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+ return GSON.fromJson(cs.getString(columnIndex), type);
+ }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyDto.java
index d4b25dab14a..19c9eee9b94 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyDto.java
@@ -19,6 +19,9 @@
*/
package org.sonar.db.sca;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.util.List;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
@@ -38,6 +41,7 @@ import static com.google.common.base.Preconditions.checkArgument;
* @param scope the scope of the dependency e.g. "development"
* @param userDependencyFilePath path to the user-editable file where the dependency was found ("manifest") e.g. package.json
* @param lockfileDependencyFilePath path to the machine-maintained lockfile where the dependency was found e.g. package-lock.json
+ * @param chains a list of the purl chains that require the dependency, stored as JSON string, e.g. [["pkg:npm/foo@1.0.0", ...], ...]
* @param createdAt timestamp of creation
* @param updatedAt timestamp of most recent update
*/
@@ -48,6 +52,7 @@ public record ScaDependencyDto(
String scope,
@Nullable String userDependencyFilePath,
@Nullable String lockfileDependencyFilePath,
+ @Nullable List<List<String>> chains,
long createdAt,
long updatedAt) {
@@ -56,6 +61,10 @@ public record ScaDependencyDto(
public static final int SCOPE_MAX_LENGTH = 100;
public static final int DEPENDENCY_FILE_PATH_MAX_LENGTH = 1000;
+ private static final Gson GSON = new Gson();
+ private static final TypeToken<List<List<String>>> CHAINS_TYPE = new TypeToken<>() {
+ };
+
public ScaDependencyDto {
// We want these to raise errors and not silently put junk values in the db
checkLength(scope, SCOPE_MAX_LENGTH, "scope");
@@ -66,6 +75,10 @@ public record ScaDependencyDto(
}
}
+ public String getChainsJson() {
+ return chains == null ? null : GSON.toJson(chains);
+ }
+
/**
* Returns the userDependencyFilePath if it is not null, otherwise returns the lockfileDependencyFilePath.
*
@@ -88,6 +101,7 @@ public record ScaDependencyDto(
private String scope;
private String userDependencyFilePath;
private String lockfileDependencyFilePath;
+ private List<List<String>> chains;
private long createdAt;
private long updatedAt;
@@ -121,6 +135,11 @@ public record ScaDependencyDto(
return this;
}
+ public Builder setChains(List<List<String>> chains) {
+ this.chains = chains;
+ return this;
+ }
+
public Builder setCreatedAt(long createdAt) {
this.createdAt = createdAt;
return this;
@@ -133,8 +152,7 @@ public record ScaDependencyDto(
public ScaDependencyDto build() {
return new ScaDependencyDto(
- uuid, scaReleaseUuid, direct, scope, userDependencyFilePath, lockfileDependencyFilePath, createdAt, updatedAt
- );
+ uuid, scaReleaseUuid, direct, scope, userDependencyFilePath, lockfileDependencyFilePath, chains, createdAt, updatedAt);
}
}
@@ -146,6 +164,7 @@ public record ScaDependencyDto(
.setScope(this.scope)
.setUserDependencyFilePath(this.userDependencyFilePath)
.setLockfileDependencyFilePath(this.lockfileDependencyFilePath)
+ .setChains(this.chains)
.setCreatedAt(this.createdAt)
.setUpdatedAt(this.updatedAt);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyReleaseDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyReleaseDto.java
index 58fcb0a1255..95682f5f688 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyReleaseDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaDependencyReleaseDto.java
@@ -19,6 +19,7 @@
*/
package org.sonar.db.sca;
+import java.util.List;
import javax.annotation.Nullable;
/**
@@ -32,6 +33,7 @@ import javax.annotation.Nullable;
* @param scope scope/type of the dep like "compile"
* @param userDependencyFilePath which manifest file (e.g. package.json)
* @param lockfileDependencyFilePath which lockfile (e.g. package-lock.json)
+ * @param chains chains that brought the dependency in, e.g. [["pkg:npm/foo@1.0.0", ...], ...]
* @param packageUrl PURL specification URL
* @param packageManager package manager
* @param packageName name of package
@@ -40,19 +42,19 @@ import javax.annotation.Nullable;
* @param known was the package known to Sonar
*/
public record ScaDependencyReleaseDto(String dependencyUuid,
- String releaseUuid,
- String componentUuid,
- boolean direct,
- String scope,
- @Nullable String userDependencyFilePath,
- @Nullable String lockfileDependencyFilePath,
- String packageUrl,
- PackageManager packageManager,
- String packageName,
- String version,
- String licenseExpression,
- boolean known
- ) {
+ String releaseUuid,
+ String componentUuid,
+ boolean direct,
+ String scope,
+ @Nullable String userDependencyFilePath,
+ @Nullable String lockfileDependencyFilePath,
+ @Nullable List<List<String>> chains,
+ String packageUrl,
+ PackageManager packageManager,
+ String packageName,
+ String version,
+ String licenseExpression,
+ boolean known) {
public ScaDependencyReleaseDto(ScaDependencyDto dependency, ScaReleaseDto release) {
this(
@@ -63,13 +65,13 @@ public record ScaDependencyReleaseDto(String dependencyUuid,
dependency.scope(),
dependency.userDependencyFilePath(),
dependency.lockfileDependencyFilePath(),
+ dependency.chains(),
release.packageUrl(),
release.packageManager(),
release.packageName(),
release.version(),
release.licenseExpression(),
- release.known()
- );
+ release.known());
if (!dependency.scaReleaseUuid().equals(release.uuid())) {
throw new IllegalArgumentException("Dependency and release UUIDs should match");
}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaDependenciesMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaDependenciesMapper.xml
index 631c269f86e..9dc0f6a15a2 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaDependenciesMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaDependenciesMapper.xml
@@ -8,6 +8,7 @@
sd.scope as scope,
sd.user_dependency_file_path as userDependencyFilePath,
sd.lockfile_dependency_file_path as lockfileDependencyFilePath,
+ sd.chains as chains,
sd.created_at as createdAt,
sd.updated_at as updatedAt
</sql>
@@ -20,6 +21,7 @@
scope,
user_dependency_file_path,
lockfile_dependency_file_path,
+ chains,
created_at,
updated_at
) values (
@@ -29,6 +31,7 @@
#{scope,jdbcType=VARCHAR},
#{userDependencyFilePath,jdbcType=VARCHAR},
#{lockfileDependencyFilePath,jdbcType=VARCHAR},
+ #{chains,jdbcType=CLOB,typeHandler=ListOfListOfStringsTypeHandler},
#{createdAt,jdbcType=BIGINT},
#{updatedAt,jdbcType=BIGINT}
)
@@ -107,6 +110,7 @@
scope = #{scope, jdbcType=VARCHAR},
user_dependency_file_path = #{userDependencyFilePath, jdbcType=VARCHAR},
lockfile_dependency_file_path = #{lockfileDependencyFilePath, jdbcType=VARCHAR},
+ chains = #{chains, jdbcType=CLOB, typeHandler=ListOfListOfStringsTypeHandler},
updated_at = #{updatedAt, jdbcType=BIGINT}
where
uuid = #{uuid, jdbcType=VARCHAR}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyDtoTest.java
index 35a163ead88..dff74348819 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyDtoTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.db.sca;
+import java.util.List;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@@ -33,6 +34,7 @@ class ScaDependencyDtoTest {
"compile",
"some/path",
"another/path",
+ List.of(List.of("pkg:npm/fodo@1.0.0")),
1L,
2L);
assertThat(scaDependencyDto).isEqualTo(scaDependencyDto.toBuilder().build());
@@ -53,6 +55,7 @@ class ScaDependencyDtoTest {
"compile",
userDependencyFilePath,
"lockfileDependencyFilePath",
+ List.of(List.of("pkg:npm/foo@1.0.0")),
1L,
2L);
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyReleaseDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyReleaseDtoTest.java
index 2eddecac392..b5653cecf99 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyReleaseDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaDependencyReleaseDtoTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.db.sca;
+import java.util.List;
import javax.annotation.Nullable;
import org.junit.jupiter.api.Test;
@@ -42,6 +43,7 @@ class ScaDependencyReleaseDtoTest {
"scope",
userDependencyFilePath,
"lockfileDependencyFilePath",
+ List.of(List.of("pkg:npm/foo@1.0.0")),
"packageUrl",
PackageManager.MAVEN,
"packageName",
diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaDependenciesDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaDependenciesDbTester.java
index 0ab6e273e15..6781bebf8c3 100644
--- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaDependenciesDbTester.java
+++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaDependenciesDbTester.java
@@ -19,6 +19,7 @@
*/
package org.sonar.db.sca;
+import java.util.List;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
@@ -53,6 +54,7 @@ public class ScaDependenciesDbTester {
"compile",
"pom.xml",
"package-lock.json",
+ List.of(List.of("pkg:npm/foo@1.0.0")),
now,
now
);