Skip to content

Commit 9c06b65

Browse files
committed
- Updated the Jenkins Freestyle UI form to add checkboxes for scan tests (SAST, SBOM, CVE), and also added a method to retrieve those details for the Pipeline method, so users can choose which tests to run from the plugin.
1 parent 48254ac commit 9c06b65

File tree

8 files changed

+164
-40
lines changed

8 files changed

+164
-40
lines changed

src/main/java/io/jenkins/plugins/ApiService.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
import java.io.OutputStream;
1010
import java.net.HttpURLConnection;
1111
import java.net.URL;
12+
import java.util.List;
1213

1314
public class ApiService {
1415

15-
public static boolean triggerScan(String token, String targetFile, EnvVars env, TaskListener listener) {
16+
public static boolean triggerScan(String token, String targetFile, List<String> scanTypes, EnvVars env, TaskListener listener) {
1617
try {
17-
URL url = new URL("https://devmiddleware.vigilnz.com/scan-targets/create");
18+
URL url = new URL("http://localhost:8000/scan-targets/multi-scan");
1819

1920
String branch = env.get("GIT_BRANCH");
2021
String repoUrl = env.get("GIT_URL");
@@ -27,28 +28,27 @@ public static boolean triggerScan(String token, String targetFile, EnvVars env,
2728
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
2829

2930
conn.setRequestMethod("POST");
30-
conn.setRequestProperty("Cookie", "__stripe_mid=8049620b-fc23-4fa0-bef6-2f457e454c507c1186; express.sid=s%3ASI1hdS10BJDFcC8Uh56X2kAS_5s_DyCA.Wrv4dKq4xj9ANv4B0EcTVtlCD96CRCK42XjjrAT2FqQ; cf_clearance=UnXvGmt.bZ52lZ.FJ78KsgODIH4Rq6f2_ocM0R8dEnw-1764075148-1.2.1.1-N.oIfJmGZpOHjfT23xgSNnJOOrCttLWtkNAC.Zth32AjfpBCn4RfCKLlRBINS3ES1AfmTiK3Mj8JK8FD8D5z9cC.1hQQJSUYrLBEJ.502c6SCnQPcui_le_Q2cFvlONWOaUdXnQje.RJceuOu1jXk7cNTd272Uc7hlbI5j6mYFaiBqPlrXfi3NJwnrsNWWZnvMNZvTV9Eo2q4LfKVW2RMKp8L3L5b5A.bw.4JID7DjM");
31+
conn.setRequestProperty("Cookie", token);
3132
conn.setRequestProperty("Content-Type", "application/json");
3233
conn.setDoOutput(true);
3334

35+
// Validate scan types
36+
if (scanTypes == null || scanTypes.isEmpty()) {
37+
listener.error("No scan types selected. At least one scan type is required.");
38+
return false;
39+
}
40+
3441
JSONObject json = new JSONObject();
35-
json.put("repositoryId", "69148c059c852f7dbff13d0c");
36-
json.put("scanType", "cve");
37-
json.put("repoName", "awesome-go");
38-
json.put("gitRepoUrl", "https://github.com/avelino/awesome-go.git");
39-
json.put("organization", "Go Projects");
40-
json.put("language", "unknown");
41-
json.put("status", "active");
42+
// Send scan types as array
43+
json.put("scanTypes", scanTypes);
44+
json.put("project", repoUrl);
45+
json.put("gitRepoUrl", repoUrl);
46+
if (targetFile != null) json.put("targetFile", targetFile);
4247

4348
String body = json.toString();
4449

45-
listener.getLogger().println("API URL: " + conn.getURL());
46-
listener.getLogger().println("Method: " + conn.getRequestMethod());
47-
listener.getLogger().println("Headers: Content-Type=" + conn.getRequestProperty("Content-Type"));
48-
listener.getLogger().println("Cookies: " + conn.getRequestProperty("Cookie"));
4950
listener.getLogger().println("Request Body: " + body);
5051

51-
listener.getLogger().println("API Run : " + conn);
5252
try (OutputStream os = conn.getOutputStream()) {
5353
os.write(body.getBytes());
5454
}
@@ -64,7 +64,7 @@ public static boolean triggerScan(String token, String targetFile, EnvVars env,
6464
while ((line = reader.readLine()) != null) {
6565
response.append(line);
6666
}
67-
listener.getLogger().println("API Response Body: " + response.toString());
67+
listener.getLogger().println("API Response Body: " + response);
6868
}
6969

7070
return responseCode == 200;

src/main/java/io/jenkins/plugins/PipelineStep.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@
99
import org.jenkinsci.plugins.workflow.steps.StepExecution;
1010
import org.kohsuke.stapler.DataBoundConstructor;
1111

12+
import java.util.List;
1213
import java.util.Set;
1314

1415
public class PipelineStep extends Step {
1516

1617
private final String token;
1718
private final String targetFile;
19+
private final List<String> scanTypes;
1820

1921
@DataBoundConstructor
20-
public PipelineStep(String token, String targetFile) {
22+
public PipelineStep(String token, String targetFile, List<String> scanTypes) {
2123
this.token = token;
2224
this.targetFile = targetFile;
25+
this.scanTypes = scanTypes != null ? scanTypes : List.of();
2326
}
2427

2528
public String getToken() {
@@ -30,6 +33,10 @@ public String getTargetFile() {
3033
return targetFile;
3134
}
3235

36+
public List<String> getScanTypes() {
37+
return scanTypes != null ? scanTypes : List.of();
38+
}
39+
3340
@Override
3441
public StepExecution start(StepContext context) throws Exception {
3542
return new PipelineStepExecution(this, context);

src/main/java/io/jenkins/plugins/PipelineStepExecution.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.nio.file.Files;
1515
import java.nio.file.Path;
1616
import java.nio.file.Paths;
17+
import java.util.List;
1718

1819
public class PipelineStepExecution extends StepExecution {
1920
private final transient PipelineStep step;
@@ -93,7 +94,17 @@ public boolean start() throws Exception {
9394
}
9495

9596
String token = creds.getToken().getPlainText();
96-
boolean result = ApiService.triggerScan(token, step.getTargetFile(), env, listener);
97+
List<String> scanTypes = step.getScanTypes();
98+
99+
// Validate at least one scan type is selected
100+
if (scanTypes == null || scanTypes.isEmpty()) {
101+
listener.error("Error: At least one scan type must be selected.");
102+
getContext().onFailure(new AbortException("At least one scan type must be selected"));
103+
return false;
104+
}
105+
106+
listener.getLogger().println("Selected Scan Types: " + String.join(", ", scanTypes));
107+
boolean result = ApiService.triggerScan(token, step.getTargetFile(), scanTypes, env, listener);
97108

98109
if (!result) {
99110
listener.error("Scan failed");

src/main/java/io/jenkins/plugins/SecurityCheckBuilder.java

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,21 @@
1515
import hudson.util.Secret;
1616
import org.kohsuke.stapler.AncestorInPath;
1717
import org.kohsuke.stapler.DataBoundConstructor;
18+
import org.kohsuke.stapler.DataBoundSetter;
1819
import org.kohsuke.stapler.QueryParameter;
1920

2021
import java.io.IOException;
2122
import java.util.Collections;
23+
import java.util.List;
2224

2325
// This file for Jenkins FreeStyle Job Method
2426
public class SecurityCheckBuilder extends Builder {
2527

2628
private final Secret token;
2729
private final String targetFile;
30+
private boolean cveScan;
31+
private boolean sastScan;
32+
private boolean sbomScan;
2833

2934
@DataBoundConstructor
3035
public SecurityCheckBuilder(Secret token, String targetFile) {
@@ -40,17 +45,81 @@ public String getTargetFile() {
4045
return targetFile;
4146
}
4247

48+
public boolean isCveScan() {
49+
return cveScan;
50+
}
51+
52+
@DataBoundSetter
53+
public void setCveScan(boolean cveScan) {
54+
this.cveScan = cveScan;
55+
}
56+
57+
public boolean isSastScan() {
58+
return sastScan;
59+
}
60+
61+
@DataBoundSetter
62+
public void setSastScan(boolean sastScan) {
63+
this.sastScan = sastScan;
64+
}
65+
66+
public boolean isSbomScan() {
67+
return sbomScan;
68+
}
69+
70+
@DataBoundSetter
71+
public void setSbomScan(boolean sbomScan) {
72+
this.sbomScan = sbomScan;
73+
}
74+
75+
public List<String> getScanTypes() {
76+
List<String> types = new java.util.ArrayList<>();
77+
if (cveScan) types.add("cve");
78+
if (sastScan) types.add("sast");
79+
if (sbomScan) types.add("sbom");
80+
return types;
81+
}
82+
4383
// this function trigger when user click the build button
4484
@Override
4585
public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
4686

4787
EnvVars env = build.getEnvironment(listener);
4888
listener.getLogger().println("------ Freestyle Method ------");
4989

50-
String tokenText = token.getPlainText(); // actual value
90+
// Build scan types list from checkboxes
91+
List<String> scanTypes = getScanTypes();
92+
93+
// Validate at least one scan type is selected
94+
if (scanTypes.isEmpty()) {
95+
listener.error("Error: At least one scan type must be selected.");
96+
return false;
97+
}
98+
99+
// Get the credential ID from the Secret
100+
String credentialId = token.getPlainText();
101+
102+
// Look up the actual TokenCredentials object
103+
TokenCredentials creds = CredentialsProvider.findCredentialById(
104+
credentialId,
105+
TokenCredentials.class,
106+
build
107+
);
108+
109+
if (creds == null) {
110+
listener.error("Error: Vigilnz Token credential not found with ID: " + credentialId);
111+
return false;
112+
}
113+
114+
// Get the actual token value from the credential
115+
String tokenText = creds.getToken().getPlainText();
116+
117+
listener.getLogger().println("Credential ID: " + credentialId);
51118
listener.getLogger().println("Your Token from Plugin: " + tokenText);
52119
listener.getLogger().println("Your Target File : " + targetFile);
53-
boolean result = ApiService.triggerScan(tokenText, targetFile, env, listener);
120+
listener.getLogger().println("Selected Scan Types: " + String.join(", ", scanTypes));
121+
122+
boolean result = ApiService.triggerScan(tokenText, targetFile, scanTypes, env, listener);
54123

55124
if (!result) {
56125
listener.error("Scan failed");
@@ -72,7 +141,7 @@ public ListBoxModel doFillTokenItems(@AncestorInPath Item project) {
72141
ListBoxModel items = new ListBoxModel();
73142

74143
for (TokenCredentials c : CredentialsProvider.lookupCredentials(TokenCredentials.class, project, ACL.SYSTEM, Collections.emptyList())) {
75-
String label = c.getTokenDescription();
144+
String label = c.getTokenId().isEmpty() ? c.getTokenDescription() : c.getTokenId();
76145
if (label == null || label.isEmpty()) {
77146
label = c.getId();
78147
}
@@ -93,6 +162,15 @@ public FormValidation doCheckToken(@QueryParameter Secret token) {
93162
}
94163
return FormValidation.ok();
95164
}
165+
166+
public FormValidation doCheckScanTypes(@QueryParameter boolean cveScan,
167+
@QueryParameter boolean sastScan,
168+
@QueryParameter boolean sbomScan) {
169+
if (!cveScan && !sastScan && !sbomScan) {
170+
return FormValidation.error("You must select at least one scan type.");
171+
}
172+
return FormValidation.ok();
173+
}
96174
}
97175

98176
}

src/main/java/io/jenkins/plugins/TokenCredentials.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ public TokenCredentials(
2424
Secret token
2525
) {
2626
super(scope, tokenId, tokenDescription);
27+
// If tokenId is null or empty, use empty string (Jenkins will handle ID generation)
28+
// This prevents errors when updating credentials that were created without an ID
29+
String idToUse = (tokenId == null || tokenId.trim().isEmpty()) ? "" : tokenId;
2730
this.token = token;
28-
this.tokenId = tokenId;
31+
this.tokenId = idToUse;
2932
this.tokenDescription = tokenDescription;
3033
}
3134

@@ -34,6 +37,10 @@ public Secret getToken() {
3437
}
3538

3639
public String getTokenId() {
40+
// If tokenId was never set by user, return the actual Jenkins-generated ID
41+
if (tokenId == null || tokenId.trim().isEmpty()) {
42+
return getId();
43+
}
3744
return tokenId;
3845
}
3946

src/main/resources/io/jenkins/plugins/SecurityCheckBuilder/config.jelly

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,17 @@
55
<c:select/>
66
</f:entry>
77

8-
<!-- Hidden area -->
9-
<div id="tokenOptions" style="display:none; margin-top:10px;">
10-
<f:entry title="Organisation">
11-
<f:textbox name="organisation"/>
12-
</f:entry>
13-
</div>
14-
8+
<f:entry field="scanTypes" title="Scan Types" description="Select at least one scan type (required)">
9+
<div style="display:flex;gap:20px;">
10+
<f:checkbox field="cveScan" title="CVE Scan"/>
11+
<f:checkbox field="sastScan" title="SAST Scan"/>
12+
<f:checkbox field="sbomScan" title="SBOM Scan"/>
13+
</div>
14+
</f:entry>
15+
<!-- <f:entry title="Project" field="projectName">-->
16+
<!-- <f:textbox/>-->
17+
<!-- </f:entry>-->
1518
<f:entry title="Target File" field="targetFile">
1619
<f:textbox/>
1720
</f:entry>
18-
19-
<script>
20-
function showTokenOptions() {
21-
document.getElementById("tokenOptions").style.display = "block";
22-
}
23-
</script>
24-
2521
</j:jelly>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div>
2+
Choose at least one scan type to analyze your project:
3+
<br><br>
4+
<li style="list-style:none;margin-left:10px;margin-bottom:5px;">
5+
<b>CVE Scan</b> : Checks for known vulnerabilities in dependencies and libraries.
6+
</li>
7+
<li style="list-style:none;margin-left:10px;margin-bottom:5px;">
8+
<b>SAST Scan</b> : Performs Static Application Security Testing to detect insecure code patterns.
9+
</li>
10+
<li style="list-style:none;margin-left:10px;">
11+
<b>SBOM Scan</b> : Generates a Software Bill of Materials to identify components and track potential risks.
12+
</li>
13+
<br>
14+
<li style="list-style:none;">
15+
Selecting multiple scan types provides broader coverage and stronger security insights.
16+
</li>
17+
</div>

src/main/resources/io/jenkins/plugins/TokenCredentials/config.jelly

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@
44
<f:entry title="Token" field="token">
55
<f:password/>
66
</f:entry>
7-
<f:entry title="ID" field="tokenId">
7+
8+
<f:entry title="ID">
89
<j:choose>
9-
<j:when test="${instance != null and instance.tokenId != null}">
10-
<f:readOnlyTextbox/>
10+
<!-- Existing credential: show read-only with actual Jenkins ID -->
11+
<j:when test="${instance != null}">
12+
<!-- Get the actual ID to display and submit -->
13+
<j:set var="idValue" value="${instance.tokenId != null and instance.tokenId != '' ? instance.tokenId : instance.id}"/>
14+
<!-- Display as read-only -->
15+
<f:readOnlyTextbox value="${idValue}"/>
16+
<!-- Submit the actual ID as hidden field (not using field="tokenId" to avoid conflict) -->
17+
<input type="hidden" name="tokenId" value="${idValue}"/>
1118
</j:when>
19+
<!-- New credential: allow user to enter ID (optional) -->
1220
<j:otherwise>
13-
<f:textbox/>
21+
<f:textbox field="tokenId"/>
1422
</j:otherwise>
1523
</j:choose>
1624
</f:entry>

0 commit comments

Comments
 (0)