diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2024-10-28 12:56:36 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-10-29 20:02:49 +0000 |
commit | 122be7bb5860e7d4d643fbce2e5d5b153da8714c (patch) | |
tree | 5bdee4e1f4cfeb2bd0516e8d398c11aedf91f7ed | |
parent | 62721916c9f1e0b2ca9a6fa3a3fbc3907834f531 (diff) | |
download | sonarqube-122be7bb5860e7d4d643fbce2e5d5b153da8714c.tar.gz sonarqube-122be7bb5860e7d4d643fbce2e5d5b153da8714c.zip |
SONAR-23485 Fix NPE when importing FOSSA CVEs
Make published_at and last_modified_at nullable
8 files changed, 176 insertions, 42 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistCveStepIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistCveStepIT.java index ad030ca4d77..faca0e5bfe6 100644 --- a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistCveStepIT.java +++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistCveStepIT.java @@ -67,12 +67,14 @@ class PersistCveStepIT { void execute_shouldInsertNewCVEs() { Cve cve1 = buildCve("1").build(); Cve cve2 = buildCve("2").build(); - batchReportReader.putCves(List.of(cve1, cve2)); + Cve cveAllOptionalEmpty = Cve.newBuilder().setCveId("CVE-empty").setDescription("Empty CVE").build(); + batchReportReader.putCves(List.of(cve1, cve2, cveAllOptionalEmpty)); persistCveStep.execute(new TestComputationStepContext()); assertCvePersistedInDatabase(cve1); assertCvePersistedInDatabase(cve2); + assertCvePersistedInDatabase(cveAllOptionalEmpty); } private void assertCvePersistedInDatabase(Cve cve) { @@ -80,11 +82,31 @@ class PersistCveStepIT { .orElseGet(() -> fail(String.format("CVE with id %s not found", cve.getCveId()))); assertThat(cveDto.id()).isEqualTo(cve.getCveId()); assertThat(cveDto.description()).isEqualTo(cve.getDescription()); - assertThat(cveDto.cvssScore()).isEqualTo(cve.getCvssScore()); - assertThat(cveDto.epssScore()).isEqualTo(cve.getEpssScore()); - assertThat(cveDto.epssPercentile()).isEqualTo(cve.getEpssPercentile()); - assertThat(cveDto.publishedAt()).isEqualTo(cve.getPublishedDate()); - assertThat(cveDto.lastModifiedAt()).isEqualTo(cve.getLastModifiedDate()); + if (cve.hasCvssScore()) { + assertThat(cveDto.cvssScore()).isEqualTo(cve.getCvssScore()); + } else { + assertThat(cveDto.cvssScore()).isNull(); + } + if (cve.hasEpssScore()) { + assertThat(cveDto.epssScore()).isEqualTo(cve.getEpssScore()); + } else { + assertThat(cveDto.epssScore()).isNull(); + } + if (cve.hasEpssPercentile()) { + assertThat(cveDto.epssPercentile()).isEqualTo(cve.getEpssPercentile()); + } else { + assertThat(cveDto.epssPercentile()).isNull(); + } + if (cve.hasPublishedDate()) { + assertThat(cveDto.publishedAt()).isEqualTo(cve.getPublishedDate()); + } else { + assertThat(cveDto.publishedAt()).isNull(); + } + if (cve.hasLastModifiedDate()) { + assertThat(cveDto.lastModifiedAt()).isEqualTo(cve.getLastModifiedDate()); + } else { + assertThat(cveDto.lastModifiedAt()).isNull(); + } assertThat(cveDto.uuid()).isNotBlank(); assertThat(cveDto.createdAt()).isNotNull(); assertThat(cveDto.updatedAt()).isNotNull(); @@ -92,8 +114,8 @@ class PersistCveStepIT { @Test void execute_shoudUpdateExistingCves() { - dbClient.cveDao().insert(dbSession, new CveDto("cve-uuid-1", "CVE-1", "Old description 1", 0.0F, 0.0F, 0.0F, 0L, 0L, 0L, 0L)); - dbClient.cveDao().insert(dbSession, new CveDto("cve-uuid-2", "CVE-2", "Old description 2", 0.0F, 0.0F, 0.0F, 0L, 0L, 0L, 0L)); + dbClient.cveDao().insert(dbSession, new CveDto("cve-uuid-1", "CVE-1", "Old description 1", 10.0, 20.0, 30.0, 0L, 0L, 0L, 0L)); + dbClient.cveDao().insert(dbSession, new CveDto("cve-uuid-2", "CVE-2", "Old description 2", null, null, null, null, null, 0L, 0L)); db.commit(); Cve cve1 = buildCve("1").build(); Cve cve2 = buildCve("2").build(); @@ -120,7 +142,7 @@ class PersistCveStepIT { @Test void execute_shouldUpdateExistingCwesAndInsertNewOnes_whenUpdatingCVEs() { - dbClient.cveDao().insert(dbSession, new CveDto("cve-uuid-1", "CVE-1", "Old description 1", 0.0F, 0.0F, 0.0F, 0L, 0L, 0L, 0L)); + dbClient.cveDao().insert(dbSession, new CveDto("cve-uuid-1", "CVE-1", "Old description 1", 0.0, 0.0, 0.0, 0L, 0L, 0L, 0L)); dbClient.cveCweDao().insert(dbSession, new CveCweDto("cve-uuid-1", "CWE-1")); dbClient.cveCweDao().insert(dbSession, new CveCweDto("cve-uuid-1", "CWE-2")); db.commit(); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistCveStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistCveStep.java index 34bd2ad9a3f..304c7280e7a 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistCveStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistCveStep.java @@ -64,7 +64,7 @@ public class PersistCveStep implements ComputationStep { public void execute(Context context) { int count = 0; try (DbSession dbSession = dbClient.openSession(false); - CloseableIterator<ScannerReport.Cve> batchCves = batchReportReader.readCves()) { + CloseableIterator<ScannerReport.Cve> batchCves = batchReportReader.readCves()) { while (batchCves.hasNext()) { updateOrInsertCve(dbSession, batchCves.next()); count++; @@ -91,18 +91,7 @@ public class PersistCveStep implements ComputationStep { } private CveDto toDtoForUpdate(ScannerReport.Cve cve, CveDto cveInDb) { - return new CveDto( - cveInDb.uuid(), - cve.getCveId(), - cve.getDescription(), - cve.getCvssScore(), - cve.getEpssScore(), - cve.getEpssPercentile(), - cve.getPublishedDate(), - cve.getLastModifiedDate(), - cveInDb.createdAt(), - system2.now() - ); + return toDto(cve, cveInDb.uuid(), cveInDb.createdAt(), system2.now()); } private void deleteThenInsertCwesIfUpdated(DbSession dbSession, ScannerReport.Cve scannerCve, String cveUuid) { @@ -122,18 +111,21 @@ public class PersistCveStep implements ComputationStep { private CveDto toDtoForInsert(ScannerReport.Cve cve) { long now = system2.now(); + return toDto(cve, uuidFactory.create(), now, now); + } + + private static CveDto toDto(ScannerReport.Cve cve, String uuid, Long createdAt, Long updatedAt) { return new CveDto( - uuidFactory.create(), + uuid, cve.getCveId(), cve.getDescription(), - cve.getCvssScore(), - cve.getEpssScore(), - cve.getEpssPercentile(), - cve.getPublishedDate(), - cve.getLastModifiedDate(), - now, - now - ); + cve.hasCvssScore() ? cve.getCvssScore() : null, + cve.hasEpssScore() ? cve.getEpssScore() : null, + cve.hasEpssPercentile() ? cve.getEpssPercentile() : null, + cve.hasPublishedDate() ? cve.getPublishedDate() : null, + cve.hasLastModifiedDate() ? cve.getLastModifiedDate() : null, + createdAt, + updatedAt); } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/dependency/CveDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/dependency/CveDto.java index 53278c024c4..e8c3cd8a21e 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/dependency/CveDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/dependency/CveDto.java @@ -19,14 +19,21 @@ */ package org.sonar.db.dependency; +import javax.annotation.Nullable; + public record CveDto( String uuid, String id, String description, - double cvssScore, - double epssScore, - double epssPercentile, + @Nullable + Double cvssScore, + @Nullable + Double epssScore, + @Nullable + Double epssPercentile, + @Nullable Long publishedAt, + @Nullable Long lastModifiedAt, Long createdAt, Long updatedAt diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index 8d87b6f6d02..272b1c156a1 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -263,8 +263,8 @@ CREATE TABLE "CVES"( "CVSS_SCORE" DOUBLE PRECISION, "EPSS_SCORE" DOUBLE PRECISION, "EPSS_PERCENTILE" DOUBLE PRECISION, - "PUBLISHED_AT" BIGINT NOT NULL, - "LAST_MODIFIED_AT" BIGINT NOT NULL, + "PUBLISHED_AT" BIGINT, + "LAST_MODIFIED_AT" BIGINT, "CREATED_AT" BIGINT NOT NULL, "UPDATED_AT" BIGINT NOT NULL ); diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AlterCveColumnsToNullableIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AlterCveColumnsToNullableIT.java new file mode 100644 index 00000000000..c2d5d754a07 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AlterCveColumnsToNullableIT.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.server.platform.db.migration.version.v108; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static java.sql.Types.BIGINT; +import static org.sonar.db.MigrationDbTester.createForMigrationStep; + +class AlterCveColumnsToNullableIT { + + private static final String TABLE_NAME = "cves"; + + @RegisterExtension + public final MigrationDbTester db = createForMigrationStep(AlterCveColumnsToNullable.class); + + private final DdlChange underTest = new AlterCveColumnsToNullable(db.database()); + + @Test + void execute_shouldUpdateConstraints() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, "published_at", BIGINT, null, false); + db.assertColumnDefinition(TABLE_NAME, "last_modified_at", BIGINT, null, false); + + underTest.execute(); + + db.assertColumnDefinition(TABLE_NAME, "published_at", BIGINT, null, true); + db.assertColumnDefinition(TABLE_NAME, "last_modified_at", BIGINT, null, true); + } + + @Test + void execute_shouldBeReentrant() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, "published_at", BIGINT, null, false); + db.assertColumnDefinition(TABLE_NAME, "last_modified_at", BIGINT, null, false); + underTest.execute(); + + underTest.execute(); + + db.assertColumnDefinition(TABLE_NAME, "published_at", BIGINT, null, true); + db.assertColumnDefinition(TABLE_NAME, "last_modified_at", BIGINT, null, true); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AlterCveColumnsToNullable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AlterCveColumnsToNullable.java new file mode 100644 index 00000000000..77d37626a66 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AlterCveColumnsToNullable.java @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.server.platform.db.migration.version.v108; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef; +import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder; + +public class AlterCveColumnsToNullable extends DdlChange { + + public static final BigIntegerColumnDef LAST_MODIFIED_COLUMN = newBigIntegerColumnDefBuilder().setColumnName("last_modified_at").setIsNullable(true).build(); + public static final BigIntegerColumnDef PUBLISHED_COLUMN = newBigIntegerColumnDefBuilder().setColumnName("published_at").setIsNullable(true).build(); + + + public AlterCveColumnsToNullable(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.execute(new AlterColumnsBuilder(getDialect(), "cves") + .updateColumn(LAST_MODIFIED_COLUMN) + .build()); + context.execute(new AlterColumnsBuilder(getDialect(), "cves") + .updateColumn(PUBLISHED_COLUMN) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java index 412306bceb7..a766c70ab7e 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java @@ -57,7 +57,8 @@ public class DbVersion108 implements DbVersion { .add(10_8_014, "Drop 'measures_migrated' column on 'project_branches' table", DropMeasuresMigratedColumnInProjectBranchesTable.class) .add(10_8_015, "Add column 'impacts' in 'active_rules' table", AddImpactsColumnInActiveRulesTable.class) .add(10_8_016, "Create 'project_dependencies' table", CreateProjectDependenciesTable.class) - .add(10_8_017, "Enable specific MQR mode", EnableSpecificMqrMode.class); + .add(10_8_017, "Enable specific MQR mode", EnableSpecificMqrMode.class) + .add(10_8_018, "Make columns 'published_at' and 'last_modified_at' nullable on the 'cves' table", AlterCveColumnsToNullable.class); } } diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto index fb7ebb72f7f..c062c0ca161 100644 --- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto +++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto @@ -238,11 +238,11 @@ message AdHocRule { message Cve { string cve_id = 1; string description = 2; - optional float cvss_score = 3; - optional float epss_score = 4; - optional float epss_percentile = 5; - int64 published_date = 6; - int64 last_modified_date = 7; + optional double cvss_score = 3; + optional double epss_score = 4; + optional double epss_percentile = 5; + optional int64 published_date = 6; + optional int64 last_modified_date = 7; repeated string cwe = 8; } |