Skip to content

Commit

Permalink
allow scanning private repos, update error handling in frontend
Browse files Browse the repository at this point in the history
Signed-off-by: Nicklas Körtge <[email protected]>
  • Loading branch information
n1ckl0sk0rtge committed Nov 20, 2024
1 parent 691115e commit 6de8642
Show file tree
Hide file tree
Showing 25 changed files with 359 additions and 87 deletions.
27 changes: 11 additions & 16 deletions frontend/src/components/global/NotificationsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,47 +31,42 @@ export default {
var kind = "error";
var title = "Unknown error";
var description = "An unknown error has occured.";
if (error == ErrorStatus.NoConnection) {
if (error.status === ErrorStatus.NoConnection) {
kind = "error";
title = "No connection";
description =
"Connection to the server has failed. Please try again later.";
} else if (error == ErrorStatus.InvalidRepo) {
} else if (error.status === ErrorStatus.InvalidRepo) {
kind = "error";
title = "Invalid repository";
description =
"The provided address does not lead to a readable repository.";
} else if (error == ErrorStatus.JsonParsing) {
} else if (error.status === ErrorStatus.JsonParsing) {
kind = "error";
title = "Parsing error";
description = "An incorrect JSON file cannot be parsed.";
} else if (error == ErrorStatus.B64Decoding) {
} else if (error.status === ErrorStatus.ScanError) {
kind = "error";
title = "Decoding error";
description = "An incorrect base 64 file cannot be decoded.";
} else if (error == ErrorStatus.BranchNotSpecified) {
kind = "error";
title = "Clone fail";
description =
"Could not clone git repo branch 'main' or 'master'. Try to specify the branch.";
} else if (error == ErrorStatus.InvalidCbom) {
title = "Error while scanning";
description = error.message;
} else if (error.status === ErrorStatus.InvalidCbom) {
kind = "error";
title = "Invalid CBOM";
description = "The provided CBOM does not respect the expected format.";
} else if (error == ErrorStatus.IgnoredComponent) {
} else if (error.status === ErrorStatus.IgnoredComponent) {
kind = "info";
title = "Some components are not shown";
description = "The provided CBOM contains one or several components that are not cryptographic assets. They are not displayed here.";
} else if (error == ErrorStatus.MultiUpload) {
} else if (error.status === ErrorStatus.MultiUpload) {
kind = "error";
title = "Multiple upload";
description = "Please only upload a single CBOM file.";
} else if (error == ErrorStatus.EmptyDatabase) {
} else if (error.status === ErrorStatus.EmptyDatabase) {
kind = "warning";
title = "Empty database";
description =
"Connection to the server was successful, but the CBOM database is empty.";
} else if (error == ErrorStatus.FallBackLocalComplianceReport) {
} else if (error.status === ErrorStatus.FallBackLocalComplianceReport) {
kind = "warning";
title = "Limited compliance results";
description =
Expand Down
59 changes: 40 additions & 19 deletions frontend/src/components/home/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
class="search-bar"
placeholder="Enter the Git URL to scan"
v-model="model.codeOrigin.gitLink"
@keyup.enter="connectAndScan(gitInfo()[0], gitInfo()[1])"
@keyup.enter="connectAndScan(advancedOptions()[0], advancedOptions()[1], advancedOptions()[2])"
/>
<cv-button
class="search-button"
:icon="ArrowRight24"
@click="connectAndScan(gitInfo()[0], gitInfo()[1])"
@click="connectAndScan(advancedOptions()[0], advancedOptions()[1], advancedOptions()[2])"
:disabled="!model.codeOrigin.gitLink"
>Scan</cv-button
>
Expand All @@ -26,18 +26,37 @@
</div>
<Transition name="filters">
<div v-show="filterOpen">
<cv-text-input
class="filter-input"
label="Branch"
placeholder="Specify a specific branch"
v-model="gitBranch"
/>
<cv-text-input
class="filter-input"
label="Subfolder"
placeholder="Specify a specific subfolder to scan"
v-model="gitSubfolder"
/>
<cv-tabs style="padding-top: 15px; padding-bottom: 10px">
<cv-tab label="Scan">
<cv-text-input
class="filter-input"
label="Branch"
placeholder="Specify a specific branch"
v-model="gitBranch"
/>
<cv-text-input
class="filter-input"
label="Subfolder"
placeholder="Specify a specific subfolder to scan"
v-model="gitSubfolder"
/>
</cv-tab>
<cv-tab label="Authentication">
<cv-text-input
class="filter-input"
label="Username"
placeholder="Leave black if you use an Access Token (PAT)"
v-model="username"
/>
<cv-text-input
type="password"
class="filter-input"
label="Password / Access Token (PAT)"
placeholder="The password for the username or an Access Token (PAT) to authenticate"
v-model="passwordOrPAT"
/>
</cv-tab>
</cv-tabs>
</div>
</Transition>
</div>
Expand All @@ -58,14 +77,16 @@ export default {
filterOpen: false,
gitBranch: null,
gitSubfolder: null,
username: null,
passwordOrPAT: null,
};
},
methods: {
gitInfo: function () {
advancedOptions: function () {
if (this.filterOpen) {
return [this.gitBranch, this.gitSubfolder];
return [this.gitBranch, this.gitSubfolder, { username: this.username, passwordOrPAT: this.passwordOrPAT }];
} else {
return [null, null];
return [null, null, null];
}
},
},
Expand All @@ -92,11 +113,11 @@ export default {
.filters-leave-active {
transition: all 0.4s;
/* max-height should be larger than the tallest element: https://stackoverflow.com/questions/42591331/animate-height-on-v-if-in-vuejs-using-transition */
max-height: 150px;
max-height: 250px;
}
.filters-enter,
.filters-leave-to {
opacity: 0;
max-height: 0px;
max-height: 0;
}
</style>
51 changes: 39 additions & 12 deletions frontend/src/helpers/scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function startWebSocket(socketURL) {
// In safari, manually closing the connection creates an error:
// Do not display an error when the connection is manually closed by the user
console.warn(
"The connection was closed by the client. An connection error occured, but has NOT been notified in the UI."
"The connection was closed by the client. An connection error occurred, but has NOT been notified in the UI."
);
} else {
console.error("WebSocket error:", error);
Expand All @@ -68,9 +68,10 @@ export function stopWebSocket() {
// }
}

export function connectAndScan(gitBranch, gitSubfolder) {
export function connectAndScan(gitBranch, gitSubfolder, credentials) {
model.resetScanningInfo();
setAndCleanCodeOrigin(gitBranch, gitSubfolder);
setCodeOrigin(gitBranch, gitSubfolder);
setCredentials(credentials)
let clientId = uuid4();
let socketURL = `${API_SCAN_URL}/${clientId}`;
startWebSocket(socketURL);
Expand All @@ -81,19 +82,30 @@ function scan() {
model.addError(ErrorStatus.NoConnection);
console.log("No socket in model");
} else if (!model.codeOrigin.gitLink) {
// TODO: Should I validate the look of the Git link in the frontend?
model.addError(ErrorStatus.InvalidRepo);
console.log("Git URL not valid");
} else {
var request = {};
request["gitUrl"] = model.codeOrigin.gitLink;
// build scan request
const scanRequest = {};
// set scan options
scanRequest["gitUrl"] = model.codeOrigin.gitLink;
if (model.codeOrigin.gitBranch) {
request["branch"] = model.codeOrigin.gitBranch;
scanRequest["branch"] = model.codeOrigin.gitBranch;
}
if (model.codeOrigin.gitSubfolder) {
request["subfolder"] = model.codeOrigin.gitSubfolder;
scanRequest["subfolder"] = model.codeOrigin.gitSubfolder;
}
model.scanning.socket.send(JSON.stringify(request));
// set credentials
if (model.credentials.pat) {
scanRequest["credentials"] = {}
scanRequest["credentials"]["pat"] = model.credentials.pat;
} else if (model.credentials.username && model.credentials.password) {
scanRequest["credentials"] = {}
scanRequest["credentials"]["username"] = model.credentials.username;
scanRequest["credentials"]["password"] = model.credentials.password;
}

model.scanning.socket.send(JSON.stringify(scanRequest));
// this.filterOpen = false
model.scanning.isScanning = true;
model.scanning.scanningStatus = STATES.LOADING;
Expand All @@ -118,11 +130,13 @@ function handleMessage(messageJson) {
); // Time in seconds
}
} else if (obj["type"] === "ERROR") {
model.addError(ErrorStatus.BranchNotSpecified); // TODO: When several different error messages will exist, this will have to be changed
model.addError(ErrorStatus.ScanError, model.scanning.scanningStatusMessage = obj["message"]); //
// update state
model.scanning.scanningStatusMessage = obj["message"];
console.error("Error from backend:", model.scanning.scanningStatusMessage);
model.scanning.scanningStatus = STATES.ERROR;
model.scanning.isScanning = false;
// log
console.error("Error from backend:", model.scanning.scanningStatusMessage);
} else if (obj["type"] === "PURL") {
model.codeOrigin.gitPurls = obj["purls"];
// This is not strictly necessary anymore now that I read PURLs from the CBOM, but it arrives before the CBOM so I leave it
Expand Down Expand Up @@ -150,7 +164,7 @@ function handleMessage(messageJson) {
}
}

function setAndCleanCodeOrigin(gitBranch, gitSubfolder) {
function setCodeOrigin(gitBranch, gitSubfolder) {
if (model.codeOrigin.gitLink) {
model.codeOrigin.gitLink = model.codeOrigin.gitLink.trim();
}
Expand All @@ -161,3 +175,16 @@ function setAndCleanCodeOrigin(gitBranch, gitSubfolder) {
model.codeOrigin.gitSubfolder = gitSubfolder.trim();
}
}

function setCredentials(credentials) {
if (credentials === null) {
return
}

if (credentials.username && credentials.passwordOrPAT) {
model.credentials.username = credentials.username;
model.credentials.password = credentials.passwordOrPAT;
} else if (credentials.passwordOrPAT) {
model.credentials.pat = credentials.passwordOrPAT;
}
}
16 changes: 13 additions & 3 deletions frontend/src/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export const model = reactive({
gitPurls: [],
uploadedFileName: null,
},
credentials: {
username: null,
password: null,
pat: null,
},
policyCheckResult: null,
errors: [],
lastCboms: [],
Expand Down Expand Up @@ -62,8 +67,13 @@ export const model = reactive({
model.codeOrigin.gitPurls = [];
model.codeOrigin.uploadedFileName = null;
},
addError(error) {
this.errors.push(error);
resetCredentials() {
model.credentials.username = null;
model.credentials.password = null;
model.credentials.pat = null;
},
addError(errorStatus, message) {
this.errors.push({status: errorStatus, message: message});
},
closeError(index) {
this.errors.splice(index, 1);
Expand All @@ -73,7 +83,7 @@ export const model = reactive({
export const ErrorStatus = {
NoConnection: "NoConnection",
InvalidRepo: "InvalidRepo",
BranchNotSpecified: "BranchNotSpecified",
ScanError: "ScanError",
JsonParsing: "JsonParsing",
InvalidCbom: "InvalidCbom",
IgnoredComponent: "IgnoredComponent",
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.16.3</quarkus.platform.version>

<sonar.crypto.plugin.version>1.3.4</sonar.crypto.plugin.version>
<sonar.crypto.plugin.version>1.3.5</sonar.crypto.plugin.version>
<sonar.plugin.api.version>10.14.0.2599</sonar.plugin.api.version>
<sonar.plugin.api.impl.version>10.7.0.96327</sonar.plugin.api.impl.version>

Expand Down
13 changes: 8 additions & 5 deletions src/main/java/com/ibm/domain/scanning/ScanAggregate.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.ibm.domain.scanning;

import app.bootstrap.core.ddd.AggregateRoot;
import com.ibm.domain.scanning.authentication.ICredentials;
import com.ibm.domain.scanning.errors.CommitHashAlreadyExists;
import com.ibm.domain.scanning.errors.InvalidGitUrl;
import com.ibm.domain.scanning.errors.ScanResultForLanguageAlreadyExists;
Expand All @@ -34,7 +35,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -51,7 +51,7 @@ private ScanAggregate(@Nonnull final ScanId id, @Nonnull final ScanRequest scanR
}

private ScanAggregate(
@NotNull ScanId id,
@Nonnull ScanId id,
@Nonnull ScanRequest scanRequest,
@Nullable Commit commit,
@Nullable Map<Language, LanguageScan> languageScans) {
Expand All @@ -63,14 +63,17 @@ private ScanAggregate(

@Nonnull
public static ScanAggregate requestScan(
@Nonnull ScanId scanId, @Nonnull final ScanRequest scanRequest) throws InvalidGitUrl {
@Nonnull ScanId scanId,
@Nonnull final ScanRequest scanRequest,
@Nullable ICredentials credentials)
throws InvalidGitUrl {
// validate value object
scanRequest.validate();
// create aggregate
final ScanAggregate aggregate =
new ScanAggregate(scanId, scanRequest); // change state: start a scan
// add domain event, uncommited!
aggregate.apply(new ScanRequestedEvent(aggregate.getId()));
aggregate.apply(new ScanRequestedEvent(aggregate.getId(), credentials));
return aggregate;
}

Expand Down Expand Up @@ -138,7 +141,7 @@ public int hashCode() {
*/
@Nonnull
public static ScanAggregate reconstruct(
@NotNull ScanId id,
@Nonnull ScanId id,
@Nonnull ScanRequest scanRequest,
@Nullable Commit commit,
@Nullable Map<Language, LanguageScan> languageScans) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* CBOMkit
* Copyright (C) 2024 IBM
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ibm.domain.scanning.authentication;

public interface ICredentials {}
Loading

0 comments on commit 6de8642

Please sign in to comment.