Skip to content

Commit 42f1709

Browse files
janbronetomi
authored andcommitted
Add required configuration for publish checks (#1584)
1 parent 1f29c50 commit 42f1709

28 files changed

+363
-96
lines changed

server/src/dev/resources/application.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,11 @@ ovsx:
179179
blocklist-check:
180180
enabled: true
181181
enforced: false
182+
required: false
182183
similarity:
183184
enabled: true
184185
enforced: false
186+
required: false
185187
similarity-threshold: 0.2
186188
skip-if-publisher-verified: false
187189
only-protect-verified-names: false
@@ -190,6 +192,7 @@ ovsx:
190192
secret-detection:
191193
enabled: true
192194
enforced: false
195+
required: false
193196
rules-path: 'classpath:scanning/secret-detection-custom-rules.yaml'
194197
suppression-markers: 'secret-detector:ignore,gitleaks:allow,nosecret,@suppress-secret'
195198
gitleaks:

server/src/main/java/org/eclipse/openvsx/admin/ScanAPI.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ public ResponseEntity<ScanResultListJson> getAllScans(
348348
var pageNumber = offset / Math.max(size, 1);
349349
var pageable = PageRequest.of(pageNumber, size, sort);
350350

351+
// Automatically include scans with errored check results when filtering by ERRORED status
352+
var includeCheckErrors = statusFilter.contains(ScanStatus.ERRORED);
353+
351354
var page = repositories.findScansFullyFiltered(
352355
statusFilter.isEmpty() ? null : statusFilter,
353356
normalizedNamespace.isEmpty() ? null : normalizedNamespace,
@@ -359,6 +362,7 @@ public ResponseEntity<ScanResultListJson> getAllScans(
359362
scannerNames,
360363
enforcedOnly,
361364
adminDecisionFilter,
365+
includeCheckErrors,
362366
pageable
363367
);
364368

@@ -869,6 +873,7 @@ private CheckResultJson toCheckResultJson(ScanCheckResult checkResult) {
869873
json.setFindingsCount(checkResult.getFindingsCount());
870874
json.setSummary(checkResult.getSummary());
871875
json.setErrorMessage(checkResult.getErrorMessage());
876+
json.setRequired(checkResult.getRequired());
872877
return json;
873878
}
874879

server/src/main/java/org/eclipse/openvsx/entities/ScanCheckResult.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ public enum CheckResult {
125125
@Column(name = "scanner_job_id")
126126
private Long scannerJobId;
127127

128+
/**
129+
* Whether this check was required (errors block publishing).
130+
* When false, errors are logged but don't block publishing.
131+
*/
132+
@Column(name = "required")
133+
private Boolean required;
134+
128135
// Getters and setters
129136

130137
public long getId() {
@@ -231,6 +238,14 @@ public void setScannerJobId(Long scannerJobId) {
231238
this.scannerJobId = scannerJobId;
232239
}
233240

241+
public Boolean getRequired() {
242+
return required;
243+
}
244+
245+
public void setRequired(Boolean required) {
246+
this.required = required;
247+
}
248+
234249
/**
235250
* Factory method for creating a passed check result.
236251
*/

server/src/main/java/org/eclipse/openvsx/json/CheckResultJson.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ public class CheckResultJson {
5757
@Schema(description = "Error message if check failed with error")
5858
private String errorMessage;
5959

60+
@Schema(description = "Whether this check was required (errors block publishing). Null for scanner jobs.")
61+
private Boolean required;
62+
6063
// Getters and setters
6164

6265
public String getCheckType() {
@@ -138,4 +141,12 @@ public String getErrorMessage() {
138141
public void setErrorMessage(String errorMessage) {
139142
this.errorMessage = errorMessage;
140143
}
144+
145+
public Boolean getRequired() {
146+
return required;
147+
}
148+
149+
public void setRequired(Boolean required) {
150+
this.required = required;
151+
}
141152
}

server/src/main/java/org/eclipse/openvsx/publish/PublishExtensionVersionHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,8 @@ private void doPublish(TempFile extensionFile, ExtensionService extensionService
272272
1, // findingsCount
273273
reason,
274274
null, // errorMessage
275-
null // scannerJobId - not a scanner job
275+
null, // scannerJobId - not a scanner job
276+
true
276277
);
277278

278279
// Also record as validation failure for the failures list

server/src/main/java/org/eclipse/openvsx/repositories/ExtensionScanRepository.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ long countForStatistics(
224224
);
225225

226226
/**
227-
* Full paginated query with ALL filters including validationType, scannerNames, enforcement, and adminDecision.
227+
* Full paginated query with ALL filters including validationType, scannerNames, enforcement, adminDecision, and check errors.
228228
*
229229
* Enforcement behavior:
230230
* - When validationType is specified: enforcement modifies that filter (AND logic)
@@ -233,7 +233,9 @@ long countForStatistics(
233233
*/
234234
@Query(value = """
235235
SELECT s.* FROM extension_scan s
236-
WHERE (CAST(:statuses AS TEXT) IS NULL OR s.status IN (:statuses))
236+
WHERE (CAST(:statuses AS TEXT) IS NULL OR s.status IN (:statuses)
237+
OR (:includeCheckErrors = true AND EXISTS (
238+
SELECT 1 FROM scan_check_result r WHERE r.scan_id = s.id AND r.result = 'ERROR')))
237239
AND (CAST(:namespace AS TEXT) IS NULL OR LOWER(s.namespace_name) LIKE LOWER('%' || :namespace || '%'))
238240
AND (CAST(:publisher AS TEXT) IS NULL OR LOWER(s.publisher) LIKE LOWER('%' || :publisher || '%'))
239241
AND (CAST(:name AS TEXT) IS NULL OR LOWER(s.extension_name) LIKE LOWER('%' || :name || '%')
@@ -276,6 +278,7 @@ Page<ExtensionScan> findScansFullyFiltered(
276278
@Param("filterAllowed") boolean filterAllowed,
277279
@Param("filterBlocked") boolean filterBlocked,
278280
@Param("filterNeedsReview") boolean filterNeedsReview,
281+
@Param("includeCheckErrors") boolean includeCheckErrors,
279282
Pageable pageable
280283
);
281284

@@ -284,7 +287,9 @@ Page<ExtensionScan> findScansFullyFiltered(
284287
*/
285288
@Query(value = """
286289
SELECT COUNT(*) FROM extension_scan s
287-
WHERE (CAST(:statuses AS TEXT) IS NULL OR s.status IN (:statuses))
290+
WHERE (CAST(:statuses AS TEXT) IS NULL OR s.status IN (:statuses)
291+
OR (:includeCheckErrors = true AND EXISTS (
292+
SELECT 1 FROM scan_check_result r WHERE r.scan_id = s.id AND r.result = 'ERROR')))
288293
AND (CAST(:namespace AS TEXT) IS NULL OR LOWER(s.namespace_name) LIKE LOWER('%' || :namespace || '%'))
289294
AND (CAST(:publisher AS TEXT) IS NULL OR LOWER(s.publisher) LIKE LOWER('%' || :publisher || '%'))
290295
AND (CAST(:name AS TEXT) IS NULL OR LOWER(s.extension_name) LIKE LOWER('%' || :name || '%')
@@ -326,7 +331,8 @@ long countScansFullyFiltered(
326331
@Param("applyAdminDecisionFilter") boolean applyAdminDecisionFilter,
327332
@Param("filterAllowed") boolean filterAllowed,
328333
@Param("filterBlocked") boolean filterBlocked,
329-
@Param("filterNeedsReview") boolean filterNeedsReview
334+
@Param("filterNeedsReview") boolean filterNeedsReview,
335+
@Param("includeCheckErrors") boolean includeCheckErrors
330336
);
331337
}
332338

server/src/main/java/org/eclipse/openvsx/repositories/RepositoryService.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,7 @@ public org.springframework.data.domain.Page<ExtensionScan> findScansFullyFiltere
850850
@Nullable Collection<String> scannerNames,
851851
@Nullable Boolean enforcedOnly,
852852
@Nullable org.eclipse.openvsx.admin.ScanAPI.AdminDecisionFilterValues adminDecisionFilter,
853+
boolean includeCheckErrors,
853854
org.springframework.data.domain.Pageable pageable
854855
) {
855856
// Convert enums to strings for native query
@@ -877,7 +878,7 @@ public org.springframework.data.domain.Page<ExtensionScan> findScansFullyFiltere
877878
startedFrom, startedTo, checkTypesParam, applyCheckTypesFilter,
878879
scannerNamesParam, applyScannerNamesFilter, enforcedOnly,
879880
applyAdminDecisionFilter, filterAllowed, filterBlocked, filterNeedsReview,
880-
pageable
881+
includeCheckErrors, pageable
881882
);
882883
}
883884

@@ -891,7 +892,8 @@ public long countScansFullyFiltered(
891892
@Nullable Collection<String> checkTypes,
892893
@Nullable Collection<String> scannerNames,
893894
@Nullable Boolean enforcedOnly,
894-
@Nullable org.eclipse.openvsx.admin.ScanAPI.AdminDecisionFilterValues adminDecisionFilter
895+
@Nullable org.eclipse.openvsx.admin.ScanAPI.AdminDecisionFilterValues adminDecisionFilter,
896+
boolean includeCheckErrors
895897
) {
896898
// Convert enums to strings for native query
897899
var statusesParam = (statuses == null || statuses.isEmpty())
@@ -917,7 +919,8 @@ public long countScansFullyFiltered(
917919
statusesParam, namespaceParam, publisherParam, nameParam,
918920
startedFrom, startedTo, checkTypesParam, applyCheckTypesFilter,
919921
scannerNamesParam, applyScannerNamesFilter, enforcedOnly,
920-
applyAdminDecisionFilter, filterAllowed, filterBlocked, filterNeedsReview
922+
applyAdminDecisionFilter, filterAllowed, filterBlocked, filterNeedsReview,
923+
includeCheckErrors
921924
);
922925
}
923926

server/src/main/java/org/eclipse/openvsx/scanning/BlocklistCheckConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ public class BlocklistCheckConfig {
5454
@Value("${ovsx.scanning.blocklist-check.enforced:true}")
5555
private boolean enforced;
5656

57+
/**
58+
* Whether errors during blocklist checking should block publication.
59+
* <p>
60+
* When true (default), exceptions during the check will block publishing.
61+
* When false, exceptions are logged but publishing continues.
62+
* <p>
63+
* Property: {@code ovsx.scanning.blocklist-check.required}
64+
* Default: {@code true}
65+
*/
66+
@Value("${ovsx.scanning.blocklist-check.required:true}")
67+
private boolean required;
68+
5769
/**
5870
* Message shown to users when their extension is blocked.
5971
* <p>
@@ -71,6 +83,10 @@ public boolean isEnforced() {
7183
return enforced;
7284
}
7385

86+
public boolean isRequired() {
87+
return required;
88+
}
89+
7490
public String getUserMessage() {
7591
return userMessage;
7692
}

server/src/main/java/org/eclipse/openvsx/scanning/BlocklistCheckService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ public boolean isEnforced() {
8585
return config.isEnforced();
8686
}
8787

88+
@Override
89+
public boolean isRequired() {
90+
return config.isRequired();
91+
}
92+
8893
@Override
8994
public String getUserFacingMessage(List<Failure> failures) {
9095
return config.getUserMessage();

server/src/main/java/org/eclipse/openvsx/scanning/ExtensionScanPersistenceService.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,20 @@ public class ExtensionScanPersistenceService {
5555
private final ObjectMapper objectMapper;
5656
private final FileDecisionRepository fileDecisionRepository;
5757
private final ScannerJobRepository scannerJobRepository;
58+
private final ScannerRegistry scannerRegistry;
5859

5960
public ExtensionScanPersistenceService(
6061
RepositoryService repositories,
6162
ObjectMapper objectMapper,
6263
FileDecisionRepository fileDecisionRepository,
63-
ScannerJobRepository scannerJobRepository
64+
ScannerJobRepository scannerJobRepository,
65+
ScannerRegistry scannerRegistry
6466
) {
6567
this.repositories = repositories;
6668
this.objectMapper = objectMapper;
6769
this.fileDecisionRepository = fileDecisionRepository;
6870
this.scannerJobRepository = scannerJobRepository;
71+
this.scannerRegistry = scannerRegistry;
6972
}
7073

7174
/**
@@ -207,7 +210,8 @@ public void recordCheckResult(
207210
int findingsCount,
208211
@Nullable String summary,
209212
@Nullable String errorMessage,
210-
@Nullable Long scannerJobId
213+
@Nullable Long scannerJobId,
214+
@Nullable Boolean required
211215
) {
212216
var checkResult = new ScanCheckResult();
213217
checkResult.setScan(scan);
@@ -222,12 +226,13 @@ public void recordCheckResult(
222226
checkResult.setSummary(summary);
223227
checkResult.setErrorMessage(errorMessage);
224228
checkResult.setScannerJobId(scannerJobId);
229+
checkResult.setRequired(required);
225230

226231
repositories.saveScanCheckResult(checkResult);
227232

228-
logger.debug("Recorded check result: {}.{} {} (scan={}) type={}, result={}, duration={}ms",
233+
logger.debug("Recorded check result: {}.{} {} (scan={}) type={}, result={}, duration={}ms, required={}",
229234
scan.getNamespaceName(), scan.getExtensionName(), scan.getExtensionVersion(),
230-
scan.getId(), checkType, result, checkResult.getDurationMs());
235+
scan.getId(), checkType, result, checkResult.getDurationMs(), required);
231236
}
232237

233238
/**
@@ -251,6 +256,10 @@ public void recordScannerJobResult(
251256
return;
252257
}
253258

259+
// Look up the scanner to get its "required" configuration
260+
Scanner scanner = scannerRegistry.getScanner(job.getScannerType());
261+
Boolean required = scanner != null ? scanner.isRequired() : true; // Default to required if scanner not found
262+
254263
recordCheckResult(
255264
scan,
256265
job.getScannerType(),
@@ -262,7 +271,8 @@ public void recordScannerJobResult(
262271
findingsCount,
263272
summary,
264273
errorMessage,
265-
job.getId()
274+
job.getId(),
275+
required
266276
);
267277
}
268278

0 commit comments

Comments
 (0)